https://pixabay.com/en/code-code-editor-coding-computer-1839406/

Increase your code coverage using Istanbul

Abha Gupta
Mar 12, 2017 · 7 min read

Often times, we talk about having 100% code coverage of our projects. Truth is, having 100% code coverage is a shining star on our codebase, but it is extremely hard to accomplish for an existing codebase for which coverage was not the topmost priority in past. The reason is very simple: when you do not write code in a ‘testable’ way, you cannot write unit tests for your code, which means, you cannot have 100% coverage.

The best way to get 100% coverage is to keep evaluating coverage as you develop the code. In a Test Driven Development (TDD), coverage comes along with the effort. However, in a non-TDD, you need to be very alert on calculating coverage as you go.

Before we go on, please note that I am not an enforcer of 100% coverage, but driving towards that goal usually enlightens us with lot of hidden flows in the code which we might skip testing otherwise.

Tools:

Since I have been working with NodeJS lately, I am going to talk about one of the most popular code coverage tool, istanbul. Assuming you can get the basics of Istanbul from its own documentation, I will just present some techniques to use Istanbul to increase the coverage of an existing project.

Istanbul presents some decent html reports which can be utilized to find code which is not covered.

Things to remember:

  1. A piece of code must be testable. If an exportable module contains a function, it should be created in a way that data can be injected from outside, so that a test can send the data and assert on the desired output. For example,

2. Conditional Statements: These are important for branch coverage. In the following example, the coverage is dropped because test case is not covering the conditional statements.

Fix it by adding a test case for else condition:

Another example with an object in picture. Usually we use “short-circuit” evaluation to set default value, but it needs to be tested as well to complete coverage.

Now a bit of complex functionality.

3. Async operations and conditions: Consider a simple example of reading a file:

Notice that coverage of Statements and Branches has decreased significantly, even though we have a test. Evaluating the report created by Istanbul, you will see something like this:

Clearly, the error scenario is not covered. The ‘I’ and ‘E’ letter signify that if-else condition was not covered for the statements, hence the branch coverage is reduced. In order to fix this, lets create a test which covers the error condition.

So coverage above is increased for Statements but Branches is still at 90%. That is because, the else part of if(data.length > 0) is not yet tested for. Lets create a test for that. However, there is a flaw in our function under test. It is not returning anything in the callback if file is found but data is empty, ie. data.length is ≤0. So we need to fix the function first :

Now the test becomes:

Example of an API test coverage:

Corresponding test

4. Async operation with conditions : Consider following api

Corresponding test:

Coverage is down because we have not yet checked for error condition.

Introducing Stubs

Stubs or mocks play very important part in unit testing. When you do not know how you can encounter an error condition in a normal scenario, you need to use a stub, basically, a mimic of original function, but which is under your control. You can force this function to throw an error the way you want.

Going back to the above scenario, we need to cover the error condition which is highlighted in pink. sinon is a popular library in Node which can be used for this purpose.

We create an object of Error and pass it on to stub as a result of the callback from find method on Todos. Notice that we are using the yields API on sinon.stub for this. yields takes the argument list that the callback should be called with.

So our complete test suite looks like below:

Summary:

A code base with 100% coverage gives your code a shield of protection against some of the unforeseen errors. Even though some errors do not make sense, the effort of covering your code 100% makes you go through certain scenarios, which you may not otherwise. And that results in a solid software product which you can be confident about.

Useful resources:

Walmart Global Tech Blog

We’re powering the next great retail disruption.