PHPackages                             faisalmirza/diff-coverage - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. faisalmirza/diff-coverage

ActiveLibrary[Testing &amp; Quality](/categories/testing)

faisalmirza/diff-coverage
=========================

Calculate code coverage for git diff (changed lines only)

v1.0.2(3mo ago)0482MITShellPHP ^8.1

Since Jan 25Pushed 3mo agoCompare

[ Source](https://github.com/faisalmirza/diff-coverage)[ Packagist](https://packagist.org/packages/faisalmirza/diff-coverage)[ Docs](https://github.com/faisalmirza/diff-coverage)[ RSS](/packages/faisalmirza-diff-coverage/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (1)Versions (4)Used By (0)

 **diff-coverage**

 [![Latest Version](https://camo.githubusercontent.com/5c80ea698346914a748b132f33f7af971487e73808ccb786a557ea4dcc48e41b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f66616973616c6d69727a612f646966662d636f7665726167652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/faisalmirza/diff-coverage) [![PHP Version](https://camo.githubusercontent.com/f803cc97dfcbc7c0edd3316997e6e5cebaf16d6fe2807b2de91549112ae5834a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f66616973616c6d69727a612f646966662d636f7665726167652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/faisalmirza/diff-coverage) [![License](https://camo.githubusercontent.com/da182fec7fa64c16f665fc1b1938bdb225090a8447820839fdab9d9cd97ca84e/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f66616973616c6d69727a612f646966662d636f7665726167652e7376673f7374796c653d666c61742d737175617265)](https://github.com/faisalmirza/diff-coverage/blob/master/LICENSE)

 Ensure new code is tested. Check coverage for only the lines you changed.

---

**diff-coverage** runs your tests with coverage and validates that changed lines meet your coverage threshold. Stop debating overall coverage percentages—focus on what matters: **new code should be tested**.

```
./vendor/bin/diff-coverage
```

```
============================================
Diff Coverage Check
============================================
Branch:        origin/main
Threshold:     100%
Coverage file: tests/coverage/clover.xml
============================================

Running tests with coverage...
✓ 142 tests passed

Checking diff coverage...
============================================
Coverage: 94.2% (threshold: 100%)

Uncovered lines:
  src/Services/PaymentService.php:45
  src/Services/PaymentService.php:46

```

Why diff-coverage?
------------------

[](#why-diff-coverage)

Traditional CoverageDiff Coverage"We need 80% overall coverage""New code must be tested"Legacy code drags down metricsOnly measures what you changedHard to improve incrementallyEvery PR can passDebates about thresholdsClear, actionable feedbackRequirements
------------

[](#requirements)

- PHP 8.1+
- Git
- Xdebug or PCOV (for coverage)

Installation
------------

[](#installation)

```
composer require --dev faisalmirza/diff-coverage
```

> **Note:** If not yet on Packagist, install from GitHub:
>
> ```
> {
>     "repositories": [
>         {"type": "vcs", "url": "https://github.com/faisalmirza/diff-coverage"}
>     ],
>     "require-dev": {
>         "faisalmirza/diff-coverage": "dev-master"
>     }
> }
> ```

Quick Start
-----------

[](#quick-start)

For Laravel, Pest, or PHPUnit projects, just run:

```
./vendor/bin/diff-coverage
```

The tool auto-detects your test framework and runs tests with coverage automatically.

Configuration
-------------

[](#configuration)

Configuration is loaded in layers (each layer overrides the previous):

```
Defaults → .diff-coverage.json → CLI arguments

```

### Option 1: Zero Configuration (Recommended)

[](#option-1-zero-configuration-recommended)

diff-coverage auto-detects your project type and enables parallel testing if `paratest` is installed:

FrameworkDetectionSource PathCoverage Path**Laravel**`artisan` file`app`, `src``tests/coverage/clover.xml`**Symfony**`bin/console` + composer.json`src``var/coverage/clover.xml`**CakePHP**`bin/cake` file`src``tmp/coverage/clover.xml`**CodeIgniter**`spark` file`app``build/coverage/clover.xml`**Yii**`yii` file`src``tests/coverage/clover.xml`**Laminas**`config/application.config.php``module`, `src``data/coverage/clover.xml`**Pest**`vendor/bin/pest``src`, `app``coverage/clover.xml`**PHPUnit**`phpunit.xml` or `vendor/bin/phpunit``src`, `app``coverage/clover.xml`**Parallel Testing:** Automatically enabled when `brianium/paratest` is installed.

### Option 2: Configuration File

[](#option-2-configuration-file)

Create `.diff-coverage.json` in your project root:

```
{
    "branch": "origin/main",
    "threshold": 80,
    "coverage_file": "tests/coverage/clover.xml",
    "test_cmd": "php artisan test --parallel --coverage-clover=tests/coverage/clover.xml",
    "source_paths": ["app", "src"]
}
```

All fields are optional. Only specify what you want to override.

FieldTypeDefaultDescription`branch`string`origin/main`Branch to compare against`threshold`integer`100`Minimum coverage percentage for changed lines`coverage_file`string(auto-detected)Path to Clover XML coverage file`test_cmd`string(auto-detected)Command to run tests with coverage`source_paths`array`["app", "src"]`Directories to check for freshness### Option 3: CLI Arguments

[](#option-3-cli-arguments)

Override any setting via command line:

```
./vendor/bin/diff-coverage [OPTIONS]
```

OptionDescriptionExample`-b, --branch`Branch to compare`-b origin/develop``-t, --threshold`Coverage threshold`-t 80``-c, --coverage`Coverage file path`-c build/coverage.xml``-T, --test-cmd`Test command`-T "phpunit --coverage-clover=cov.xml"``-f, --force`Force re-run tests`-f``-s, --skip-tests`Use existing coverage`-s``-h, --help`Show help`-h`Usage Examples
--------------

[](#usage-examples)

```
# Use auto-detected defaults
./vendor/bin/diff-coverage

# Compare against develop branch with 80% threshold
./vendor/bin/diff-coverage -b origin/develop -t 80

# Skip running tests (use existing coverage file)
./vendor/bin/diff-coverage -s

# Force re-run tests even if coverage file is fresh
./vendor/bin/diff-coverage -f

# Custom test command
./vendor/bin/diff-coverage -T "vendor/bin/phpunit --testsuite=unit --coverage-clover=coverage.xml"
```

Performance
-----------

[](#performance)

### Freshness Detection

[](#freshness-detection)

diff-coverage automatically skips running tests if your coverage file is newer than your source files. This speeds up repeated runs during development.

```
$ ./vendor/bin/diff-coverage
Coverage file is fresh, skipping tests
(use --force to re-run tests)
```

To always run tests: `./vendor/bin/diff-coverage -f`

To never run tests: `./vendor/bin/diff-coverage -s`

CI Integration
--------------

[](#ci-integration)

### GitHub Actions

[](#github-actions)

```
name: Tests

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Required for git diff

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          coverage: xdebug

      - name: Install dependencies
        run: composer install

      - name: Check diff coverage
        run: ./vendor/bin/diff-coverage -t 80
```

### GitLab CI

[](#gitlab-ci)

```
diff-coverage:
  stage: test
  script:
    - composer install
    - ./vendor/bin/diff-coverage -b origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME -t 80
  only:
    - merge_requests
```

### Bitbucket Pipelines

[](#bitbucket-pipelines)

```
pipelines:
  pull-requests:
    '**':
      - step:
          name: Diff Coverage
          script:
            - composer install
            - ./vendor/bin/diff-coverage -b origin/main -t 80
```

Exit Codes
----------

[](#exit-codes)

CodeMeaning`0`Coverage meets threshold (or no changes detected)`1`Coverage below threshold, tests failed, or configuration errorHow It Works
------------

[](#how-it-works)

1. **Detect** your test framework (Laravel/Pest/PHPUnit)
2. **Run** tests with coverage (unless skipped or fresh)
3. **Generate** git diff against the target branch
4. **Filter** coverage report to only changed lines
5. **Calculate** coverage percentage for those lines
6. **Exit** with success/failure based on threshold

Under the hood, diff-coverage uses [exussum12/coverage-checker](https://github.com/exussum12/coverage-checker) for the diff filtering logic.

Troubleshooting
---------------

[](#troubleshooting)

### "No changes detected"

[](#no-changes-detected)

Your branch has no diff against the target branch. This is normal for the main branch.

### "Coverage file not found"

[](#coverage-file-not-found)

Tests may have failed, or the coverage file path is incorrect. Check:

- Tests pass when run directly
- The coverage file path in your config matches where your tests write it

### Coverage seems wrong

[](#coverage-seems-wrong)

Ensure you're comparing against the correct branch:

```
./vendor/bin/diff-coverage -b origin/main  # not just 'main'
```

### Tests run every time

[](#tests-run-every-time)

The freshness check compares timestamps. If your CI always clones fresh, tests will always run. This is expected behavior in CI.

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit a Pull Request.

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

39

—

LowBetter than 86% of packages

Maintenance78

Regular maintenance activity

Popularity18

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

113d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/5e7f37c287d6cd30cecad7c6155c01e26d39d1089a8b95308b7701ff9d980afb?d=identicon)[faisalmirza](/maintainers/faisalmirza)

---

Top Contributors

[![faisalmirza](https://avatars.githubusercontent.com/u/1070807?v=4)](https://github.com/faisalmirza "faisalmirza (12 commits)")

---

Tags

testingphpunitdiffpestcoveragegitci

### Embed Badge

![Health badge](/badges/faisalmirza-diff-coverage/health.svg)

```
[![Health](https://phpackages.com/badges/faisalmirza-diff-coverage/health.svg)](https://phpackages.com/packages/faisalmirza-diff-coverage)
```

###  Alternatives

[rregeer/phpunit-coverage-check

Check the code coverage using the clover report of phpunit

606.1M179](/packages/rregeer-phpunit-coverage-check)[ockcyp/covers-validator

Validates @covers tags in PHPUnit tests

21198.0k82](/packages/ockcyp-covers-validator)[hot/phpunit-runner

The lib allows to watch phpunit tests

3066.9k4](/packages/hot-phpunit-runner)[vipsoft/code-coverage-extension

Behat code coverage collector

2186.5k2](/packages/vipsoft-code-coverage-extension)[mrpunyapal/rector-pest

Rector upgrade rules for Pest - refactoring and best practices for Pest testing framework

4717.6k18](/packages/mrpunyapal-rector-pest)[code-distortion/adapt

A Laravel package that builds databases for your tests, improving their speed.

2835.5k](/packages/code-distortion-adapt)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
