How much code coverage is enough?

This is something I have seen come up in conversations a few times in online forums and discussions. How much code coverage is enough? There isn’t a simple, straight-forward answer to this question though.

Ideally you would be aiming for 100% code coverage, such that every line of code in the code-base is exercised by at least one unit test. But line coverage is not the only code coverage measurement.

I recently ran into an issue where a particular function was failing. I was surprised as the function was covered by several unit tests, and so I would have thought that any problems with the function would have been picked up by one or more of the unit tests. After some investigation I soon discovered that the problem was the result of the function being invoked with arguments that were causing it to fail. Whilst the arguments were perfectly valid, they were in a format that the function wasn’t expecting.

Simply put, the output from the first function was the input to the second function. And whilst both functions were unit tested independently and both gave positive results, what was missing was a unit test where the first function invoked the second function. This unit test would have quickly highlighted the issue earlier.

After finding the issue, it was quickly fixed and subsequent unit tests have now been written to test for this particular scenario. So it’s always important to be aware of how data flows through your application. It is not sufficient to unit test all the functions in isolation, when in reality there exists a network of inter-connected functions all invoking each other in different ways.

So by all means, unit test your data layer and ensure that it gives the correct output from the specified input. But you also need to be sure that your data layer gives the correct results when invoked from your business layer, and that your business layer gives the correct results when invoked from the user-interface layer.

Measuring your code coverage by line coverage is a blunt instrument. Knowing how those functions are invoked, and testing those scenarios is equally important. Basically, you need end-to-end coverage. Test your data layer is giving the correct results by invoking the user-interface layer, and tracing the execution path all the way through the application.

It’s not the quantity of your unit tests that is important, but the quality of those unit tests.

If this article was helpful to you, please do hit the 💚 button below. Thanks!