Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Code Coverage Laravel Package

phpunit/php-code-coverage

phpunit/php-code-coverage collects, processes, and renders PHP code coverage data. It provides drivers, filters, and report generation for PHPUnit test suites, including serialized coverage data and OpenClover output, helping teams track which lines of code tests execute.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require --dev phpunit/php-code-coverage
    

    Add to composer.json under require-dev if only needed for testing.

  2. Basic Usage:

    use SebastianBergmann\CodeCoverage\CodeCoverage;
    use SebastianBergmann\CodeCoverage\Driver\Selector;
    use SebastianBergmann\CodeCoverage\Filter;
    use SebastianBergmann\CodeCoverage\Report\Facade;
    
    $filter = new Filter;
    $filter->includeFiles([__DIR__ . '/src/**/*.php']);
    
    $coverage = new CodeCoverage(
        (new Selector)->forLineCoverage($filter),
        $filter
    );
    
    $coverage->start('Test Suite');
    // Execute your tests or code here
    $coverage->stop();
    
    Facade::fromObject($coverage)->renderHtml(__DIR__ . '/coverage-report');
    
  3. Key Classes:

    • CodeCoverage: Core class for collecting coverage data.
    • Filter: Define which files/methods to include/exclude.
    • Report\Facade: Generate reports (HTML, XML, etc.).
    • Serialization\Serializer/Unserializer: Save/load coverage data.

Implementation Patterns

Daily Workflows

1. Test-Driven Coverage

  • Pattern: Integrate coverage into CI/CD pipelines.
// In your test bootstrap file (e.g., tests/bootstrap.php)
$coverage = new CodeCoverage(
    (new Selector)->forLineCoverage(),
    new Filter
);
$coverage->start('Unit Tests');

// Run tests
$result = (new TestRunner)->run();

$coverage->stop();
Facade::fromObject($coverage)->renderCrap(__DIR__ . '/coverage-crap.txt');

2. Custom Report Generation

  • Pattern: Extend Report\Facade for custom formats.
Facade::fromObject($coverage)->render(
    new CustomReportRenderer(),
    __DIR__ . '/custom-report.json'
);

3. Filtering Logic

  • Pattern: Use Filter to focus on critical paths.
$filter = new Filter;
$filter->includeFiles([__DIR__ . '/src/Service/*.php'])
       ->excludeFiles([__DIR__ . '/src/Service/Logger.php']);

4. Serialization for CI

  • Pattern: Save coverage data for later analysis.
$serializer = new Serializer();
$serializer->serialize($coverage, __DIR__ . '/coverage.data');

// Later...
$unserializer = new Unserializer();
$data = $unserializer->unserialize(__DIR__ . '/coverage.data');
Facade::fromSerializedData($data)->renderHtml(__DIR__ . '/report');

5. Branch/Path Coverage

  • Pattern: Enable deeper analysis.
$coverage = new CodeCoverage(
    (new Selector)->forBranchCoverage($filter),
    $filter
);

Integration Tips

Laravel-Specific Use Cases

  1. Service Provider Hook:

    // In AppServiceProvider
    public function boot()
    {
        if ($this->app->environment('testing')) {
            $coverage = new CodeCoverage(
                (new Selector)->forLineCoverage(),
                new Filter
            );
            $coverage->start('Laravel Tests');
    
            // Run tests via PHPUnit
            $coverage->stop();
            Facade::fromObject($coverage)->renderHtml(storage_path('coverage'));
        }
    }
    
  2. Artisan Command:

    // app/Console/Commands/GenerateCoverage.php
    public function handle()
    {
        $coverage = new CodeCoverage(
            (new Selector)->forLineCoverage(),
            new Filter
        );
        $coverage->start('Artisan Command');
    
        // Execute logic
        $coverage->stop();
        Facade::fromObject($coverage)->renderCrap(storage_path('coverage-crap.txt'));
    }
    
  3. Pest Integration:

    // pest.php
    beforeTests(function () {
        $coverage = new CodeCoverage(
            (new Selector)->forLineCoverage(),
            new Filter
        );
        $coverage->start('Pest Tests');
    });
    
    afterTests(function () use ($coverage) {
        $coverage->stop();
        Facade::fromObject($coverage)->renderHtml(storage_path('pest-coverage'));
    });
    

Gotchas and Tips

Pitfalls

  1. Driver Conflicts:

    • Issue: Using both xdebug and pcov can cause invalid XML.
    • Fix: Ensure only one driver is active. Use Selector to explicitly choose:
      (new Selector)->forLineCoverage()->withDriver('xdebug');
      
  2. Path Coverage Overhead:

    • Issue: Path coverage (forPathCoverage()) significantly slows down tests.
    • Tip: Use sparingly and only for critical paths.
  3. UTF-8 Validation:

    • Issue: Non-UTF-8 source files may break XML reports.
    • Fix: Ensure your source files are UTF-8 encoded.
  4. Race Conditions:

    • Issue: Parallel test runners (e.g., Pest with --parallel) may cause race conditions in coverage data.
    • Fix: Use CachingSourceAnalyser or serialize coverage data per test suite.
  5. Quadratic Merge Performance (fixed in 14.4.1+):

    • Issue: In versions prior to 14.4.1, CodeCoverage::merge() copies all previously merged tests on every call, leading to quadratic time complexity when merging coverage from many processes (e.g., parallel test runs).
    • Fix: Upgrade to phpunit/php-code-coverage 14.4.1 or later. If stuck on an older version, avoid calling merge() repeatedly; instead, serialize each process’s coverage and merge once at the end, or aggregate via a single process.
  6. Abstract Methods:

    • Issue: Abstract methods are incorrectly counted as executable lines.
    • Fix: Update to v14.1.2+ where this is fixed.
  7. Attribute Lines:

    • Issue: Lines spanned by attributes (e.g., #[Test]) are treated as executable.
    • Fix: Update to v14.1.1+ for proper handling.
  8. Dark Mode HTML Reports:

    • Issue: Custom colors may not work in dark mode.
    • Fix: Use the built-in colorblind-friendly palette or override via CSS.

Debugging Tips

  1. Verify Coverage Data:

    $summary = Facade::fromObject($coverage)->summary();
    $this->assertGreaterThan(80, $summary->getLineCoverageInPercent());
    
  2. Inspect Filter Rules:

    • Use Filter::getIncludedFiles() and Filter::getExcludedFiles() to debug inclusion/exclusion logic.
  3. Check Driver Compatibility:

    • Run with --debug flag in PHPUnit to see active drivers:
      phpunit --debug
      
  4. Validate XML Reports:

    • Use xmllint to validate generated XML:
      xmllint --noout coverage.xml
      
  5. Performance Bottlenecks:

    • Profile report generation with Facade::fromObject($coverage)->renderHtml() to identify slow steps.
    • For parallel runs, measure merge() duration; upgrade to 14.4.1+ if you see quadratic growth.

Extension Points

  1. Custom Report Formats:

    • Extend SebastianBergmann\CodeCoverage\Report\Renderer\RendererInterface:
      class CustomRenderer implements RendererInterface {
          public function render(CodeCoverage $coverage, string $path): void {
              // Custom logic
          }
      }
      
  2. Filter Extensions:

    • Override SebastianBergmann\CodeCoverage\Filter methods like isFileIncluded() for dynamic rules.
  3. Driver Plugins:

    • Implement SebastianBergmann\CodeCoverage\Driver\DriverInterface for custom coverage drivers (e.g., for PHP 8.3+).
  4. Serialization Hooks:

    • Extend SebastianBergmann\CodeCoverage\Serialization\Serializer to add custom metadata:
      $serializer = new class($coverage) extends Serializer {
          protected function getAdditionalData(): array {
              return ['custom_key' => 'custom_value'];
          }
      };
      
  5. HTML Report Customization:

    • Override templates in the SebastianBergmann\CodeCoverage\Report\Html namespace or extend the Renderer class.

Laravel-Specific Quirks

  1. Storage Paths:

    • Use storage_path() for consistent coverage report storage:
      Facade::fromObject($coverage)->renderHtml(storage_path('coverage'));
      
  2. Artisan Commands:

    • Cache coverage data to avoid recomputation:
      if (!file_exists($coveragePath)) {
          $coverage->stop();
          Facade::fromObject($coverage)->renderHtml($coveragePath);
      }
      
  3. CI/CD Integration:

    • Use phpunit/php-code-coverage in combination with phpunit/phpunit for seamless CI integration:
      # .github/workflows/coverage.yml
      - name: Run tests with coverage
        run: phpunit --coverage-clover=coverage.xml
      
    • For parallel test runs (e.g., ParaTest, Pest --parallel), upgrade to phpunit/php-code-coverage 14.4.1+ to avoid quadratic merge() overhead when consolidating results.
  4. Pest Parallel Testing:

    • Pest’s --parallel uses multiple processes. Ensure you merge coverage data efficiently:
      // After all parallel runs, merge serialized coverage files
      $coverage = new CodeCoverage(...);
      foreach (glob(storage_path('coverage/*.data')) as $file) {
          $coverage->merge((new Unserializer)->unserialize(file_get_contents($file)));
      }
      Facade::fromObject($coverage)->renderHtml(storage_path('coverage'));
      
    • In versions before 14.4.1, merge one large batch rather than incrementally to mitigate the quadratic cost.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core