About Talks Articles

The Hidden Trap of Code Coverage

3 min read

Code which never gets tested is an accident waiting to happen. The coverage of the code as it is exercised by the suite becomes an important metric. It is not a surprise if everyone aims to hit that magical 100% coverage. This applies also to the world of front-end web development with JavaScript.

I’ve written previously on the use of coverage tool to ensure Esprima code quality (with a minor twist that the coverage tool is also based on Esprima). This allows us to claim that we reach the maximum possible statement coverage since the set of 500 unit tests touches every meaningful statement in the code.

While statement coverage is important and you should definitely try to hit its peak, care must be taken to ensure that this is not the only coverage analysis being considered. As an illustration, let’s have a look at the following function:

function inc(p, q) {
    if (q == undefined) q = 1;
    return p + q/q;

The task of this function is very simple. I’m sure you can quickly notice the glaring bug in that function. This is intentional to demonstrate the point. Please do realize that in real-word scenarios, functions tend to be bigger and a logic mistake is often hidden in multiple layer of complexity.

To test that function, a simple assertion should be enough:

assert("inc(4) must give 5", inc(4) == 5);

The test reporter will not complain since it will pass with flying colors. If the test library supports checking the code coverage of the function, it will also say that both the if statement (including its body containing the assignment to q) and the return statement are executed. From the statement coverage point of view, such a reporter will say Congrats, 100% covered!.

At this point, if you stop writing more tests because you are confident of the achievement, you will never expose the bug in the function. Only if you add another test, e.g checking for inc(4, 2), you would realize that something is fishy with the implementation.

Looking at the code, how is the situation different if it would have been written like the following?

function inc(p, q) {
    if (q == undefined) {
        return p + 1;
    } else {
        return p + q/q;

Running the same test will still mark it as pass but now the coverage analyzer loudly complains that you only hit one of the return statements. It’s definitely not 100% covered. You are then forced to continue writing more tests which hit another return statement and eventually reveal the bug.

Why is this possible? Mainly because typical statement coverage analysis does not reveal the actual code sequence. In the above example, there are two code paths to the end of the function and the unit test only follows one of them. The two code paths are unfortunately not obvious in the first version of the code.

As I always said, we still need to push JavaScript tools to the next level. I hope someone out there is working on JavaScript LCSAJ.

Update: Now you can use Istanbul to track JavaScript branch coverage. Very useful!

Related posts:

♡ this article? Explore more articles and follow me Twitter.

Share this on Twitter Facebook