Consider the following example file test.py
:
def fun(l):
for i in l:
a = 1
print(a)
if __name__ == '__main__':
fun([1])
Testing branch coverage with
coverage erase; coverage run --branch test.py ; coverage html
There are no complains about the for loop although it is not tested with an empty list. If it was the program would crash with "UnboundLocalError: local variable 'a' referenced before assignment". Since I have asked coverage for branch coverage instead of statement coverage I would expect this to be reported. Why does coverage not report this?
Maybe it helps to understand how branch coverage works:
When measuring branches,
coverage.py
collects pairs of line numbers, a source and destination for each transition from one line to another. Static analysis of the source provides a list of possible transitions. Comparing the measured to the possible indicates missing branches.
1| def fun(l):
2| for i in l:
3| a = 1
4| print(a)
There are two branches here:
The fun([1])
call in the test actually fully covers both branches. On the first iteration of the loop, we have line 2->3, and on the final iteration, we have jump 2->4.
Remember that 100% coverage does not imply the code actually works in all cases, it just means that all those lines and branches have been visited during the test suite.
If you modify this code so that only one of the branches of the for
gets visited, you will see a partial cover highlighted on L3: