Code coverage statistics are often heavily relied upon in an effort to gauge testing efforts and infer relative code quality. But, as I’ve written about before , code coverage reports can often fool you. As such, I highly recommend you use these reports not as a means for judging what’s tested but in judging what’s not tested.

It turns out that code conditionals are a bit tricky when it comes to measuring code coverage and in certain scenarios, tests can trigger high line and branch coverage rates but fail to uncover nefarious defects. In fact, I recently posted a code example that demonstrates short-circuiting code coverage via a simple OR condition .

In my posting’s code example, the second clause in an if conditional throws an exception; however, because one can easily write a test that only logically executes the first clause of the conditional, the second part of the conditional is skipped, yet a coverage tool may end up reporting 100% branch coverage as it did for me. The question then arises– how can one infer the quality of tests if code coverage reports are often liberal in their calculations?

One highly effective technique that yields much more effective testing (and therefore more precise coverage) is the notion of path testing. By measuring paths through a method (which, in essence, is the Cyclomatic complexity value of the method) one can quickly gauge how many tests one would need to achieve adequate coverage.

For example, running the Eclipse Metrics plug-in against this method yields a CC of 3. This means there are 3 logical paths through this method and accordingly, I’d need to write at least 3 tests to accurately test the branchIt method.

The branchIt method isn’t terribly complex, but look closely at its logic and see if you can spot the 3 paths.

public void branchIt(int value){
 if((value > 100) || (HiddenObject.doWork() == 0)){
  this.dontDoIt();
 }else{
  this.doIt();
 }
}  

It seems the 3 paths are divided as follows:

  • The first conditional has two clauses, hence there’s 2 paths
  • The later else conditional

The good news is that you’d find the defect rather quickly in this case– the mere act of trying to trigger the else clause will almost force the defect to show up, won’t it? What’s also interesting is that if you look at the Cobertura report for this code, you’ll notice that it correctly notes that the else conditional wasn’t covered in my first test. If I were trying to achieve 100% line coverage, I’d quickly uncover the defect lurking in the first clause by trying to force the else.