handcraftedinthealps/code-coverage-checker
CLI tool to enforce PHPUnit code coverage thresholds. Generate a coverage.php report, then check line/class/method coverage against a minimum percentage, optionally limited to specific directories—ideal for CI to fail builds when coverage drops.
Install the package in your Laravel project:
composer require --dev handcraftedinthealps/code-coverage-checker
Generate a coverage report during PHPUnit execution:
phpunit --coverage-php Tests/reports/coverage.php
Ensure the Tests/reports/ directory exists and is writable.
Run the checker in your CI/CD pipeline or local workflow:
vendor/bin/code-coverage-checker Tests/reports/coverage.php "line" "90.0"
Replace "line" with "class" or "method" as needed, and adjust the threshold (e.g., "90.0").
Add this to your phpunit.xml or CI script (e.g., GitHub Actions):
<!-- phpunit.xml -->
<phpunit>
<listeners>
<listener class="\PHPUnit\TextUI\Command" file="--coverage-php=Tests/reports/coverage.php"/>
</listeners>
</phpunit>
Then, in your CI script:
phpunit
vendor/bin/code-coverage-checker Tests/reports/coverage.php "line" "90.0" || exit 1
This fails the build if coverage drops below 90%.
Tests/reports/coverage.php: Generated by PHPUnit; required for the checker.vendor/bin/code-coverage-checker: CLI tool for validation.phpunit.xml: Configure coverage generation (e.g., --coverage-clover or --coverage-php).Configure PHPUnit in phpunit.xml:
<phpunit>
<coverage>
<include>
<directory>./app</directory>
<directory>./src</directory>
</include>
<exclude>
<directory>./tests</directory>
</exclude>
</coverage>
<listeners>
<listener class="\PHPUnit\TextUI\Command" file="--coverage-php=storage/coverage.php"/>
</listeners>
</phpunit>
storage/coverage.php (avoid Tests/ for cleaner project structure).Add a Custom Task in composer.json:
"scripts": {
"test": [
"phpunit",
"@check-coverage"
],
"check-coverage": "vendor/bin/code-coverage-checker storage/coverage.php line 95.0"
}
Run with:
composer test
Enforce stricter coverage for critical paths (e.g., app/Services/):
vendor/bin/code-coverage-checker storage/coverage.php line 90.0 app/Services/
app/Services/ are checked against the threshold.Use .env or CI variables:
COVERAGE_THRESHOLD=${COVERAGE_THRESHOLD:-90.0} \
vendor/bin/code-coverage-checker storage/coverage.php line $COVERAGE_THRESHOLD
For Symfony projects, leverage the symfony/phpunit-bridge:
php bin/phpunit --coverage-php=var/tests/coverage.php
vendor/bin/code-coverage-checker var/tests/coverage.php method 98.0
If using phpunit-parallel, generate a single merged report:
phpunit --coverage-php=storage/coverage-merged.php --coverage-clover=storage/coverage.clover
vendor/bin/clover-to-php storage/coverage.clover > storage/coverage-merged.php
vendor/bin/code-coverage-checker storage/coverage-merged.php line 90.0
phpunit/clover-to-php to merge reports.Artisan Command: Create a custom command to wrap the checker:
// app/Console/Commands/CheckCoverage.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
class CheckCoverage extends Command
{
protected $signature = 'coverage:check {threshold : Coverage threshold (e.g., 90.0)}';
protected $description = 'Check code coverage against a threshold';
public function handle()
{
$process = new Process([
'vendor/bin/code-coverage-checker',
'storage/coverage.php',
'line',
$this->argument('threshold')
]);
$process->run();
if (!$process->isSuccessful()) {
$this->error($process->getOutput());
exit(1);
}
$this->info('Coverage check passed!');
}
}
Run with:
php artisan coverage:check 90.0
Laravel Forge/Envoyer: Add the check to deployment scripts:
php artisan test
php artisan coverage:check 90.0 || exit 1
Report Path Issues
File not found: Tests/reports/coverage.php--coverage-php in PHPUnit to debug:
phpunit --coverage-php=storage/debug-coverage.php
[ -f "storage/coverage.php" ] || (echo "Coverage report missing!" && exit 1)
Symfony/PHPUnit-Bridge Conflicts
Class not found: PHPUnit\TextUI\Commandsymfony/phpunit-bridge is installed and compatible:
composer require --dev symfony/phpunit-bridge
composer.json to avoid conflicts:
"require-dev": {
"symfony/phpunit-bridge": "^6.0",
"phpunit/phpunit": "^9.5"
}
Threshold Granularity
90.0 vs. 90.00).90 instead of 90.0) or round in your script:
vendor/bin/code-coverage-checker storage/coverage.php line "$(echo 90.5 | cut -d. -f1).0"
Root Directory Reports
app/) may not parse correctly.vendor/bin/code-coverage-checker storage/coverage.php line 90.0 --exclude=app/
(Note: --exclude is not natively supported; use directory-specific thresholds instead.)CI Cache Invalidation
rm -f storage/coverage.php
phpunit --coverage-php=storage/coverage.php
vendor/bin/code-coverage-checker storage/coverage.php line 90.0
Verbose Output
Run the checker with -v for debug info:
vendor/bin/code-coverage-checker -v storage/coverage.php line 90.0
Checking file: app/Models/User.php (95.2% coverage)
Inspect the Coverage File
The coverage.php file is a PHP array. Inspect it directly:
php -a <<< '$f = file_get_contents("storage/coverage.php"); eval("?>".file_get_contents($f)); print_r($coverage);'
List Uncovered Files Use v0.2.9+ to list files below threshold:
vendor/bin/code-coverage-checker storage/coverage.php line 90.0 --
How can I help you explore Laravel packages today?