Note: I have edited this entry on July 19, 2004 to clean-up the code after someone pointed out some of the errors below.
A bit of reading lately about code coverage, and aiming for 100% and whether that is realistic or not...
It's important for people to step back and not just a code coverage, but the quality of the unit test cases that they are writing...
We'll look at a function to take 2 strings, convert the strings to numbers, add them together and return the result as a string (yes - we all know that when coverting strings to numbers to add them together, you should take a Vector... just kidding... for demonstration purposes only folks...)
Ex:
/**
* Add 2 numbers two numbers together...
*/
public String addNumbers(String value1, String value2) {
int i1 = Integer.parseInt(value1);
int i2 = Integer.parseInt(value2);
return("" + (i1+i2));
}
We could write a testcase:
public void testAddNumbers() {
int i1 = 5;
int i2 = 10;
assertTrue((""+(i1+i2)).equals(addNumbers(""+i1,""+i2)));
}
Bingo ... I've tested that my function works, and I've got 100% code coverage...
But what happens when I take user input and pass it directly into my function ... And the user doesn't type a number... Let's simulate in our testcase...
public void testAddNumbers() {
int i1 = 5;
int i2 = 10;
assertTrue((""+(i1+i2)).equals(addNumbers(""+i1,""+i2)));
assertTrue(("NaN".equals(addNumbers("bob","doug")));
}
Hmmm... My function is throws a NumberFormat exception... and my test case is clearly failing... We'll update our function:
/**
* Add 2 numbers two numbers together...
* @return sum of two numers, or "NaN" if either string is not a number...
*/
public String addNumbers(String value1, String value2) {
try {
int i1 = Integer.parseInt(value1);
int i2 = Integer.parseInt(value2);
return("" + (i1+i2));
} catch (NumberFormatException nfe) {
// debug: nfe.printStackTrace();
return("NaN");
}
}
Now my testcase is passing, I'm back at 100% code coverage, and all is wonderful... Until the guy in the next cubicle start's passing null's into my function...
We update the testcase:
public void testAddNumbers() {
int i1 = 5;
int i2 = 10;
assertTrue((""+(i1+i2)).equals(addNumbers(""+i1,""+i2)));
assertTrue("NaN".equals(addNumbers("bob","doug")));
assertTrue("NaN".equals(addNumbers("bob",null)));
}
Now I'm getting NullPointerExceptions...
Update the function:
/**
* Add 2 numbers two numbers together...
* @return sum of two numers, or "NaN" if either string is not a number...
*/
public String addNumbers(String value1, String value2) {
try {
int i1 = Integer.parseInt(value1);
int i2 = Integer.parseInt(value2);
return("" + (i1+i2));
} catch (NumberFormatException nfe) {
// debug: nfe.printStackTrace();
return("NaN");
} catch (NullPointerException npe) {
// debug: npe.printStackTrace();
return("NaN");
}
}
... You get the point... This can go on for quite a while ... Someone might pass in "4.5" and "3" and expect "7.5" - but won't get it because our implementation is using Integers... We've had 100% code coverage all along, but found that some input throws Runtime exceptions, that if uncaught, would bring our program down entirely...
While code coverage is a great and noble goal we should all strive for, it is no guarentee that the code won't fail...