What is Path Coverage?
Branch coverage tells you whether each outcome of each decision ran. Path coverage tells you whether each combination of outcomes ran: every route from the start of a method to its end.
function shippingCost(Order $order): int
{
$cost = 5;
if ($order->total > 100) {
$cost = 0;
}
if ($order->isExpress()) {
$cost += 10;
}
return $cost;
}
Two tests give 100% branch coverage here: one with a large express order, and one with a small normal order. The method has four paths, though. A large normal order and a small express order never ran, so path coverage is 50%.
Each if doubles the paths through a method, so path coverage is low by nature.
A suite with 80-90% branch coverage often has a path coverage of about 5%. That is normal, not a fault in the counting.
Use it to compare commits and to find methods with many untested combinations, not as a target to reach 100%.
How OtterWise Tracks Path Coverage
Path coverage is available only with Pebble, the coverage collector of OtterWise Raft. There is nothing to turn on in OtterWise. When the report contains path data, we read it on every upload and store path coverage for each commit, file and method.
- Commits, branches and pull requests show a Path Coverage value next to Branch Coverage, with a chart on the repository page.
- The file list shows a Paths column. Hover a value to see the paths taken out of all paths.
- The code view shows the paths taken on each method with more than one path, for example
Paths 2/4. - Partly covered lines: to show where the missing paths are, Pebble also writes how many outcomes of each decision ran. A decision line where some outcomes did not run gets a badge with the counts, for example "Branches 1/2". The badge is red when no outcome ran and amber when some did. A line that ran, but not all of its outcomes, is also marked as partly covered, in amber.
Path coverage does not change Code Coverage, the element total, or status checks. It also gets no good or bad color, because the usual coverage thresholds do not apply to it.
Enable it with Pebble
Run your tests with --path-coverage and write a Clover or Cobertura report:
pebble run --path-coverage -- vendor/bin/phpunit --coverage-clover=build/logs/clover.xml
Path coverage includes branch coverage, so you get both from one run. Upload the report as usual.
Why only Pebble?
PHPUnit with Xdebug and --path-coverage collects paths, but only shows them in its text and HTML reports.
Clover and Cobertura have no field for paths. Pebble adds them as extra attributes, which other tools ignore.
What we read
| Format | Attributes |
|---|---|
| Clover XML | paths and coveredpaths on each file's <metrics> and on each <line type="method">. |
| Cobertura XML | paths-valid and paths-covered on the root <coverage>, each <package> (file) and each <method>. The CRAP index of a method then uses its path coverage, as PHPUnit does. |
Troubleshooting
If path coverage is missing, open your coverage report and check:
- Clover: the file-level
<metrics>elements should have apathsattribute. - Cobertura: the root
<coverage>should have apaths-validattribute. - Pebble: make sure the run uses
--path-coverage(orPEBBLE_PATHS=1).--branch-coveragealone records no paths.
Still stuck? Reach out and include a snippet of your coverage report.