Hey there! The issue of the day discusses the differences between line and branch coverage.
Have you ever wondered why your code still had bugs despite having 100% test coverage? Today, let’s talk about one of the reasons why testing coverage metrics can sometimes be deceiving by introducing the concepts of line and branch coverage:
Line coverage: Measures the percentage of code statements executed during testing.
Branch coverage: Checks if we took the true and false branch for each condition (if condition, loops, etc.).
Consider this seemingly harmless example1:
public int getNameLength(boolean isCoolUser) {
User user = null;
if (isCoolUser) {
user = new John();
}
return user.getName().length();
}
If we add a test on this function with isCoolUser
set to true, line coverage will generate 100%. But is our code really valid? Indeed, what would happen if isCoolUser
was false? In this case, our function would lead to a null pointer exception.
This is a known limit with line coverage, a metric that is used too often. However, branch coverage will detect that our test only covers 50% of the possible branches, hence allowing us to either be more cautious about this code or enrich the test battery.
If you have the opportunity, use both types of coverage. Line coverage will track all the lines, and branch coverage will help us not to miss edge cases. Also, remember that code quality is more than just green tests.
Tomorrow, we will explore the close relationship between unit tests and documentation.
The example is taken from Stack Overflow.
I remember in some cases we added empty else statements (like with a no-op call) to ensure branch coverage was good using line coverage tools. It's a bit like if you have `if a || b`, you should test the different combinations and not just the test passing once.