Code Coverage - more than just a number
Code coverage
Code coverage is a set of tools, that examine the percentages of code covered by the tests. I’m working with Jest on daily basis, in this article I will use examples from this test framework.
To run coverage report, you can use following command:
jest --coverage
You can see the results right away in your terminal or open nicer GUI with coverage in src/coverage/lcov-report/index.html
The metrics
- lines - simply how much of you code is taking part in the tests
- statements - same as above but it counts only instructions
- functions - how many functions were called during test
- branches - all branches of if/else, switches, conditional flows
Lets look at silly example:
// Test case
describe('isErrorRoute - helper to determine if url from input is recognized as error route in routing', () => {
it('should', () => {
expect(isErrorRoute('https://example.com/error')).toEqual(true)
expect(isErrorRoute('https://example.com/error/not-found')).toEqual(true)
})
})
// Implementation
export const isErrorRoute = (url?: string) => {
if (!url) {
// 1. branch not covered
// 2. both 'statements' in line 4 and 5 also not covered
console.log('statement')
return false
}
return url.includes('/error')
}
And the test with following metrics
- lines - 60% (3/5)
- statements - 66.66% (4/6)
- functions - 100% (1/1)
- branches - 0% (0/1)
What information code coverage gives us ?
- what parts of application are not covered with tests at all
- acts as a general guide for further analysis, where there might a be a real need of adding tests
What information code coverage won’t give you
- how much behaviour is covered
- it tell you nothing about overall tests quality
- covering code with tests ≠= covering functionality with tests
The code coverage requirements
Most of the times there is requirement of having 80% code coverage in order to add new functionality to the codebase.
While I can understand the purpose of it, there is a huge chance, that some developers may focus solely on meeting the 80% threshold, without considering the real purpose of testing.
Common gotchas to spot on for “80% threshold focused” devs:
- Over-reliance on mocks and stubs
- No assertions in the tests
- Writing many very similar tests
- Ignoring code paths that are difficult to test (which may be a crucial parts for functionality)
- Testing implementation details rather than behavior