I have a code-coverage requirement of of a certain percentage, and face the following tradeoff:
Should I sacrifice in-method sanity checks and error handling for ease of (unit-) testability?
Lets consider two variants of a simple method addAndSquare.
Variant A:
double addAndSquare(double a, double b ){
return (a+b)*(a+b);
}
This method is wonderfully easy to test for the standard behaviour that is intended. In a unit test one could simply pass it two exemplary non-trivial values and check for the wanted result. The testcoverage of percentage X is very easy to achieve. It is, however, not the case that the method would check for corner cases during runtime, and react accordingly. Say we pass the method a NAN or INF, or produce some overflow error, then the method would merrily pass on wrong results to the outside world.
Variant B:
double addAndSquare(double a, double b ){
if(a == null || b == null) throw ...
if(a > Double.MAX_VALUE) throw new ArithmeticException("double overflow");
if(b > Double.MAX_VALUE) throw new ArithmeticException("double overflow");
...
double result = (a+b)*(a+b);
if(result == null) ...
if(result > Double.MAX_VALUE) ...
return result;
}
Now, this variant will be more safe during production runtime, it will immediately handle a bad state as it occurs, and will make finding the underlying error a lot easier. It will also gracefully end the program before anything gets broken in the realworld. The drawback however is, that this is horribly annoying to write tests for and reaching coverage X since you'd have to cover all the if's and the try-catch situations.
I come from a background where I'd want to know if anything is wrong immediately, but recently I observed that devs more experienced than me lay their focus much more on the testing side than on the runtime-sanity-checks side.
What am I missing?
a
andb
asdouble
(notDouble
). They can never benull
. You probably got a warning from the compiler about that.Double.POSITIVE_INFINITY
or NAN or whatever. Sometimes you can just let these values percolate through the code and end up in the result, or other times it's better to catch them as early as they happen (so you have the most context for how to prevent those values, if they weren't expected). If you opt for the latter approach, you should make yourself a helper function so you're not repeating yourself, something likethrowIfOverflowed(a);