Monday, January 29, 2007

Assorted hacks

I've been coding batch services the last couple of days, and found a few useful tricks and tools for testing that I would to share with you, the reader :-).

The task is basically to extract a number of filenames from an XML file (part of a complete processing of the file), and to download those files from a web server and store locally. We're already using XmlBeans to map the XML to JavaBeans, so I can stub the elements that I'm interested in for a particular test. XmlBeans generates interfaces for every node, so I let Eclipse create default implementations of the interfaces I need, which I then override inline like this:

Foo foo = new Foo() {
@Override
public Bar getBar() {
return new Bar() {
@Override
public String getStringValue() {
return "test-bar-value";
}
}
}
...
}

to represent (stub out) an XML fragment on this form:

<foo>
<bar>test-bar-value</bar>
...
</foo>

I've decided that I want to write my test against an actual HTTP server, since mocking that part is more work and less return on investment. Now, I don't want to run the test against the production server, and I don't want to rely on the fact that a dedicated Apache has been started either. The solution is to run an HTTP server as part of the test!

There are a number of different Java implementations of HTTP servers, ranging from microscopic to full-blown. I settled for Jetty, which is small, fast, has a nice programmatic API and you can rely on it to handle request-headers correctly, which might not be the case for YetAnotherSuperSmallHttpServer.sourceforge.net.

Anyway, a minimal HTTP file server can be accomplished with the following code:

Server server;

public void setUp() {
server = new Server(55555);
ResourceHandler resourceHandler =new ResourceHandler();
resourceHandler.setResourceBase(
new ClassPathResource("webroot").getFile().getAbsolutePath());
server.addHandler(resourceHandler);
server.start();
}

public void tearDown() {
if (server != null) {
server.stop();
}
}

The resource base is set to the directory "webroot" in the class path (ClassPathResource is a Spring class), which resolves to src/test/resources/webroot when you're using the Maven standard directory layout (like I do). The server runs on localhost on port 55555 for the duration of the test.

Make sure that you have execution rights on all directories from the root down to the actual directory you serve. Many Linux distributions (Fedora, Debian) sets 700 on the home directories. If you want to browse the server, add a simple System.in.read(); after server.start(). At least in Eclipse you can press any key when the console window is active to continue, I'm sure the other two have similar capabilities.

The actual downloading is done using Jakarta HttpClient. I don't have anything particular to say about that, it has a nice API and seems very solid and broadly used.

One important aspect of the task at hand is to not download images unless they are newer than the copy that we already have, so I'm using the "If-Modified-Since" header. Finding the correct date format was harder than I expected (the RFC specs don't count, who reads 'em anyway). Here's a snippet that seems to do the trick:

private static final SimpleDateFormat httpDateFormat = new SimpleDateFormat("EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US);
static {
httpDateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
}

Finally, a rant and a warning about java.util.Calendar: if you populate your database with a timestamp "2007-01-02 12:30:00", here's how you do an assertEquals() in a test:

Calendar calendar = Calendar.getInstance();
// Yes, only month numbering starts with 0
calendar.set(2007, 0, 2, 12, 30, 0);
// Yes, there's no set() that includes milliseconds
calendar.set(Calendar.MILLISECOND, 0);
// Yes, we have no bananas
assertEquals(calendar, myLoadedCalendarValue);

This last thing is nothing new, but it deserves a daily beating.

No comments: