Tuesday, September 26, 2006

Unit Testing + Code Coverage = Bad For Business?

Measuring code coverage is a natural complement to unit testing. It provides a clear picture of what parts of your code remain untested, and helps you focus additional tests on the uncovered code. Even with just a few unit tests you can quickly cover the basic functionality of several methods in your class under test. Unfortunately, a common mistake is to measure coverage without actually verifying that the executed code is correct, and this is exactly what happens with many code coverage tools.

Consider this example:



This is a simple class that converts an array of integers into the corresponding array of Strings. Note that there is an intentional bug in getIntegerName() -- case 1 of the switch is missing a break statement and causes the method to return the wrong value.

We've created a couple of JUnit tests to start with.



We'll use Coverlipse, a popular open-source coverage tool for Eclipse, to help us measure statement coverage for these unit test. Here are the Coverlipse results.



Both JUnit tests ran successfully and Coverlipse reports 100% statement coverage for the Sample class. Looking at the detailed coverage markers in the Eclipse editor, Coverlipse reports that all statements have been covered. It looks like the perfect testing summary to show to the boss, but a deeper analysis reveals some serious problems with these tests.

Most serious, obviously, is that we did not find the bug in getIntegerName(). In this case, our test assertions missed checking for the appropriate "one" value, but it could just as easily have been the case that the method under test masked the incorrect value returned by getIntegerName(). The real reason that the bug was not found is that no assertions were made about the subordinate method.

The createIntegerNameList() method is invoked by our tests. That's the unit under test, and the only method for which we should be making assertions. The getIntegerName() method was obviously executed, but because we didn't verify its behavior, we missed an obvious problem. Until we've verified the subordinate method directly, we should consider it untested.

Even when this coverage technique is successful in uncovering bugs, it's an inefficient way to conduct testing. Imagine trying to thread a needle from ten feet away. Eventually you'll be able to do it, but how many attempts will fail first? Similarly, testing a subordinate method via its invoker is coarse work at best and impossible at worst. In our example it's easy to see how we can drive down into the subordinate method, but as a method's logic increases, this task becomes increasingly difficult. If you're examining the logic of the invoking method to determine how to increase the coverage of the subordinate method, why not just examine (and test) the logic of the subordinate method directly? Don't waste time with test that might give you a minimal boost in coverage. Test the underlying method directly.

Now let's look at the same test suite using a technique that reports path coverage for unit tests in isolation.



Notice that no coverage has been reported for getIntegerName(), which is what we expect since we haven't created any unit tests for it. Until we examine and test each of the paths identified in this method, our testing effort remains incomplete.

Unit tests are so named for a reason -- concentrate on coverage only for the unit under test and ignore all others.

4 Comments:

Anonymous Ron said...

Hi,

I came across your post, and was the kind of verification I was looking for. I'm preparing a demo for my cohorts on how code coverage and unit testing coincide. I encountered a situation where the method being tested invokes a subordinate method which throws an exception for a given condition.

So my dillema was should I write a test that expects exception, even though it's not coming from the method being tested? Should I re-write the method being tested to try the subordinate, catch the exception, and throw it from there?

Or should I prepare my test method and mock object so the chances of an exception occuring in the subordinate method is nil?, if that's possible.

Any thoughts on that would be appreciated. I would like to get to a point to where I can say the subordinate method is not the concern because that is not what's being tested, so if it throws an exception cause of malformed data, well that's what it is supposed to do.

Thanks for any input,
Ron

Fri Jan 26, 01:45:00 AM 2007  
Blogger John Miller said...

It's certainly valid to write a test that expects the exception from the higher-level method, but the point here is that you shouldn't call the higher-level method in order to measure coverage on the subordinate one. If you can get complete coverage on the subordinate method first, then you can be sure that any exceptions thrown by it are legitimate and expected. Testing them both by calling the higher-level method should be considered integration testing, not unit testing.

Sat Jan 27, 10:18:00 AM 2007  
Anonymous Ron said...

Thanks for the response. I believe my first intention was to unit test the subordinate method, but it's visibility is private, and therefore untestable by the NUnit framework.

Does the need to test it indicate that it should be made public?

Wed Jan 31, 03:43:00 PM 2007  
Blogger Codign Software said...

There are ways around that so you can test private methods, at least with JUnit. While we have a product to do that for JUnit tests, a good article circa 2004 was written on this topic as well: http://www.artima.com/suiterunner/private.html

Hope that helps.

Wed Jan 31, 09:37:00 PM 2007  

Post a Comment

<< Home