JUnit 4.7 @Rules!!!

Wilfred Springer

It took me a while before I really understood what this @Rule business in JUnit 4.7 is really about, but I like it!

Last week, I had to make sure that I could test a RESTful web service client. So, all I really wanted is to make sure that a web server would always return a particular response. However, since the test would be executed on a continuous integration server, there was - as always - the risk of port conflicts.
So this is what I ended up doing:

Creating a Web Server

Well, not really a full web server, but rather a wrapper around Jetty, and called it WebServer. However, this WebServer implemented MethodRule. Now, this is the trick. If in your test class, you create a field pointing to an object implementing the MethodRule interface, and you mark this to be processed as a rule, by adding the @Rule implementation, then JUnit will call back on your instance for every test it will run, allowing you to add additional behavior around your test execution.
interface MethodRule {
Statement apply(final Statement base, FrameworkMethod method, Object target)
}
My WebServer class implements this method by starting and stopping Jetty. This is what it says in my test class:
@Rule
public WebServer server =
new WebServer("WEBSERVER_PORT", 9191);
Notice that my WebServer constructor contains two parameters. The first parameter contains the name of an environment variable that might exist. If it does exist, then the value of that environment variable will be used as the port number for the web server. The second parameter is the default port number, to be used in case the port number is not set by the environment variable.

Environment Variables?

The reason for having the ability to pass in an environment variable here is that this allows Hudson to make sure that you don't have any port conflicts. Now, obviously, with all of this I don't know in advance which port number is going to be used. That's why my Web Server also implements a method getURL() which will return a String representation of the resource that we are going to hit. I don't have to keep track of that port number. The WebServer will just tell me which port it's using.

Which Resource?

Now, I can already hear the next question coming: how would the WebServer know which resource it needs to server? That a good question. With all of this working, you would prefer not having to worry about this from your test. (I mean, you could obviously further configure the Web Server from your test, but that would be kind of awkward, since it's already running.)

Annotations to the rescue

The answer turns out to be easier than you might expect: you just use another annotation. Now, this is not something that @Rule is dictating or anything, but I feel this is going to be an emerging pattern. I basically tell the WebServer which resource to serve by having an annotation on my test method telling it.
So my test looks like this:
@Test
@WebResource(content="classpath:whatever.xml", contentType="text/xml")
public void shouldBeAbleToDealWithWhatever {
String url = server.getURL();
// Download something from that URL using your client
}
Notice the classpath: prefix here. This is a little something borrowed from Spring. This will force the WebServer to locate the resource on the classpath. If you happen to run your tests from Maven, then this is an excellent way of letting the Web Server know that it should look into src/test/resources.

Conclusions

@Rule rules. It works beautifully. And using annotations on your test methods to parameterize the execution of your rules makes it even more useful. (Source code has been uploaded here: http://code.google.com/p/junit-webtest-rule/.)

Comments (0)

    Add a Comment