What tests taught me about my code
Back to basics, or maybe somewhere I hadn't been before. It may sound like a cliché, and to some extent it is, but show me your tests and I'll tell you what kind of programmer you are. If I ever said that most tests were integration tests by their very nature for most of the enterprise applications I've written, I was badly mistaken. Today I'd amend that: Yes, we need integration tests too, but at least for Java code, unit tests should considerably outnumber them. The problem with writing integration tests at the expense of unit tests is that we probably find them easier to write because our code is procedural.
When I worked on HP SOA Systinet, we had whole batteries of integration tests. I'd guess they made up 95% of the tests. To me, that of course includes tests that need a container to start, such as a Spring application context. Thank goodness we had those tests, but they and other integration tests tell us something different about the code we write. It took a lot of coding to make that mental leap.
Why favor unit tests?
Unit tests push us to program in an object-oriented way. Integration tests don't make us do anything in particular. We just take the top layer of spaghetti and test it. The result is that the code underneath gets less coverage, because the tests are written against the top layer. A unit test pushes us to untangle the spaghetti into objects with responsibilities of their own, which we can then mock easily.
Another effect of writing unit tests is that you spend much more time thinking about the interfaces between your objects. That's the kind of thing that gets obscured when you're writing spaghetti code.
Say we have a class that downloads an RSS file from a given URL and returns the post titles. A simplified version might look something like this.
public class RssDownloader {
public String[] getTitles(Url rssUrl) {
HttpClient httpClient = new HttpClient();
Xml xml = new Xml(httpClient.get(rssUrl));
String titles[] = new String[];
Node items[] = xml.getElements("Items");
for(Node item : items) {
titles.add(item.getTitle());
}
httpClient.close();
return titles;
}
}The problem with this class is that it mixes two things: downloading the RSS file and processing it. We should keep processing the XML separate from where it came from. What if we suddenly need to read the RSS from a file system or a database? It's a poor object-oriented design, but we may not realize that until we try to test it. Then we have to write an integration test and start an HTTP server somewhere. What we really want to test is that the code reads what we expect from the given XML, rather than how the HTTP client works.
The next illustration shows the right way to do it. Original illustration
public class RssDownloader { private final RssTitleExtractor titleExtractor;public RssDownloader() { this.titleExtractor = new RssTitleExtractor(); }
public RssDownloader(RssTitleExtractor ext) { this.titleExtractor = ext; }
public String[] getTitles(Url rssUrl) { HttpClient httpClient = new HttpClient(); Xml xml = new Xml(httpClient.get(rssUrl)); String titles[] = titleExtractor.getTitles(xml); httpClient.close(); return titles; } }
public static class RssTitleExtractor {
public String[] getTitles(Xml xml) {
String titles[] = new String[];
Node items[] = xml.getElements(“Items”);
for(Node item : items) {
titles.add(item.getTitle());
}
return titles;
}
}You can then test the processing without an integration test. In fact, you can and should mock the XML abstraction. You could also make the HTTP client an instance variable, mock it, and test whether RssDownloader uses it correctly, for example by checking the call to close.
This simple example shows how object-oriented programming, here illustrated by properly separating the responsibilities of objects, lets us test the code easily with unit tests.
Medium version · Archived original page
Related articles
Related CZ Podcast episodes
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi and Filemon discuss Java 6 and web application frameworks. · Czech audio
- #1 · Vývojová prostředí v Javě
The first CZ Podcast episode looks at Java development environments. · Czech audio