Projections for PHPUnit Coverage Report

Recently at my company we’re pushing for more automated testing and one of the metrics we’re looking at is, of course, code coverage. Although I’m not a big fan of code coverage as a metric, it at least gives you a general idea how well you’re doing. If you’ve worked with PHPUnit before, you definitely have generated a code coverage report for a project. These reports are great, because you can easily spot the parts in your code missing test coverage and tackle these.

At my company, our project is a large – like 10k+ PHP files large – and we have multiple teams working on different areas of that codebase. This makes it a bit tricky when it comes to code coverage, because although it’s great to know how well we’re doing overall, me as a team lead would also like to know how well my team is doing. Besides that, we have different architectural layers and requirements regarding code coverage are different for each.

Wouldn’t it be great to have a dedicated report for each team or each layer?

The most obvious solution to this problem would be to have different phpunit.xml configs whith code coverage whitelist/exclude rules for the files you’re interest in. Yes, that would work, but it is not very efficient to run the tests X times to generate each of those reports and maintain all these config files.

I thought there must be a better way to do this …

At our company we have automated code attribution, which means that each file in the project can be attributed to a team. In addition, we follow some conventions that require code from specific architectural layers to be located under specific paths. This gives us a handy set of file paths patterns, which can be mapped to a team or architectural layer. Great, so we have a list of file path patterns that we’re interested in.

Next thing we need is a code coverage report. Not a normal one, but something that is machine readable. For this use-case PHPUnit supports a PHP report, which is a dump of all the coverage data collected. You can get it by adding --coverage-php=coverage.php when executing PHPUnit.

Now we need to get this all together, and all we need is a little script that takes this data, filters it with the file path patterns and generates a coverage report from it. Thankfully, PHPUnit is build in a very modular way, so we can do this. Here’s an example:

<?php
use SebastianBergmann\CodeCoverage\CodeCoverage;
use SebastianBergmann\CodeCoverage\Report\Html\Facade;

/** @var CodeCoverage $codeCoverage */
$codeCoverage = require 'coverage.php';

$filterFunction = function (string $filePath): bool {
    return true; // Here you need to make the decision if the file should be in the report or not
};

$whiteListedFilesFiltered = array_filter($codeCoverage->filter()->getWhitelistedFiles(), $filterFunction, ARRAY_FILTER_USE_KEY);
$dataFiltered = array_filter($codeCoverage->getData(), $filterFunction, ARRAY_FILTER_USE_KEY);

$coverageFiltered = new CodeCoverage;
$coverageFiltered->setData($dataFiltered);
$coverageFiltered->setTests($codeCoverage->getTests());
$coverageFiltered->filter()->setWhitelistedFiles($whiteListedFilesFiltered);

// Generate the HTML report for the projected code coverage
$writer = new Facade;
$writer->process($coverageFiltered, $targetDir);

Voilà, there we have our projected HTML code coverage report.

Since you have all the code coverage data still in coverage.php, you can have as many projections as you want.