unit testing

Why You Need Test Coverage, But Shouldn’t Trust It

I’ve never heard someone say that unit tests—or any kind of tests—are useless. Practically everyone agrees that testing our software is a good idea. There are objections about the amount of time it takes to set up the testing environment and create tests, but let’s assume you’ve gotten past all of that and are actually writing tests. How do we know that we’ve tested everything we need to? Tests can’t help if there are parts of your code that aren’t tested. That brings us to the topic of this article: test coverage.

What Is It?

There are tools out there—probably for every programming language, but we’ll focus on JavaScript—that run alongside your tests and track which code is executed during the tests. Don’t ask me how it’s done; it’s all magic as far as I’m concerned, but it works, even on code that is compiled/transformed as long as there are source maps available. Most of these tools are able to give you statistics on which lines, statements, and code branches have been executed, so you can know what parts of your project could use more testing.

So how do you get started with integrating test coverage tools into your testing? In some cases, such as with the Jest unit testing framework or with scaffolding tools such as Vue CLI, it’s built right in so you don’t have to do anything extra to enable code coverage, at least for your unit tests. Apart from that, you can use popular test coverage tools like Istanbul, Blanket.js, or JSCover. There are also tools that go beyond plain test coverage such as Sealights, which tracks coverage statistics over time and offers other enterprise-level features. However, it has a cost, so choose one of the free and open-source projects if you’re not looking for anything super advanced. To learn how to install the tools, you’ll have to check out each individual project’s site for their instructions.

The Problem With Test Coverage

While using test coverage tools can be considered by many to be essential, there’s something you’ll need to be aware of. Just because a test coverage tool tells you that something is covered, that’s not the end of the story. Coverage simply informs you what code was executed, not whether you are adequately testing to make sure the code produced the correct outcome.

Here are a few situations where coverage can be misleading:

if (a || b) {
} else {

In this example, if you test with a being true and b being false, and do another test where they are both false, then your results will come back saying that you’ve covered 100% of this code. But you haven’t tested the case where b is true and a is false. This may or may not be important depending on your code, but it’s important to test numerous cases to ensure there are no unexpected side effects.

Here’s another example:

eventEmitter.subscribe('event', () => foo('event'))
// ... more code ...

If this code is run by any of your tests, then your coverage tool will report that everything is covered, but if you don’t test to ensure that the execution of that event listener had the correct effects, then it isn’t fully tested.


Should you still use test coverage tools since they don’t give you perfectly accurate information? Yes! They may tell you something is tested when it technically isn’t, but if they say some code isn’t covered, then it certainly isn’t covered, and you’ll know to create tests that cover that code. This is quite useful even if sometimes they can give you a false confidence in your testing. In other words, if you aren’t checking your test coverage, it is likely in your best interest to do so.