In addition to the usual statement coverage, coverage.py also supports branch coverage measurement. Where a line in your program could jump to more than one next line, coverage.py tracks which of those destinations are actually visited, and flags lines that havenât visited all of their possible destinations.
For example:
1def my_partial_fn(x): 2 if x: 3 y = 10 4 return y 5 6my_partial_fn(1)
In this code, line 2 is an if
statement which can go next to either line 3 or line 4. Statement coverage would show all lines of the function as executed. But the if was never evaluated as false, so line 2 never jumps to line 4.
Branch coverage will flag this code as not fully covered because of the missing jump from line 2 to line 4. This is known as a partial branch.
How to measure branch coverage¶To measure branch coverage, run coverage.py with the --branch
flag:
coverage run --branch myprog.py
When you report on the results with coverage report
or coverage html
, the percentage of branch possibilities taken will be included in the percentage covered total for each file. The coverage percentage for a file is the actual executions divided by the execution opportunities. Each line in the file is an execution opportunity, as is each branch destination.
The HTML report gives information about which lines had missing branches. Lines that were missing some branches are shown in yellow, with an annotation at the far right showing branch destination line numbers that were not exercised.
The XML and JSON reports produced by coverage xml
and coverage json
also include branch information, including separate statement and branch coverage percentages.
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.
The idea of tracking how lines follow each other was from Titus Brown. Thanks, Titus!
Excluding code¶If you have excluded code, a conditional will not be counted as a branch if one of its choices is excluded:
1def only_one_choice(x): 2 if x: 3 blah1() 4 blah2() 5 else: # pragma: no cover 6 # x is always true. 7 blah3()
Because the else
clause is excluded, the if
only has one possible next line, so it isnât considered a branch at all.
Sometimes branching constructs are used in unusual ways that donât actually branch. For example:
while True: if cond: break do_something()
Here the while loop will never exit normally, so it doesnât take both of its âpossibleâ branches. For some of these constructs, such as âwhile True:â and âif 0:â, coverage.py understands what is going on. In these cases, the line will not be marked as a partial branch.
But there are many ways in your own code to write intentionally partial branches, and you donât want coverage.py pestering you about them. You can tell coverage.py that you donât want them flagged by marking them with a pragma:
i = 0 while i < 999999999: # pragma: no branch if eventually(): break
Here the while loop will never complete because the break will always be taken at some point. Coverage.py canât work that out on its own, but the âno branchâ pragma indicates that the branch is known to be partial, and the line is not flagged.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4