PHPackages                             shipmonk/phpunit-parallel-job-balancer - 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. shipmonk/phpunit-parallel-job-balancer

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

shipmonk/phpunit-parallel-job-balancer
======================================

Balances PHPUnit test execution across parallel jobs based on JUnit XML timing data

1.0.0(5mo ago)9802↓37.5%[1 issues](https://github.com/shipmonk-rnd/phpunit-parallel-job-balancer/issues)MITPHPPHP ^8.1CI passing

Since Dec 3Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/shipmonk-rnd/phpunit-parallel-job-balancer)[ Packagist](https://packagist.org/packages/shipmonk/phpunit-parallel-job-balancer)[ RSS](/packages/shipmonk-phpunit-parallel-job-balancer/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (1)Dependencies (11)Versions (3)Used By (0)

PHPUnit Parallel Job Balancer
=============================

[](#phpunit-parallel-job-balancer)

Balances PHPUnit test execution across parallel jobs based on historical timing data from JUnit XML reports.

This tool helps optimize CI pipelines by distributing tests evenly across parallel runners, minimizing the total execution time.

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

[](#installation)

```
composer require --dev shipmonk/phpunit-parallel-job-balancer
```

Usage
-----

[](#usage)

### Basic Usage

[](#basic-usage)

```
vendor/bin/phpunit --log-junit junit.xml
vendor/bin/balance-phpunit-jobs junit.xml
```

This outputs PHPUnit testsuite XML fragments to stdout. Copy them into your `phpunit.xml` to configure parallel test runs.

### Options

[](#options)

OptionShortDescriptionDefault`--jobs=N``-j`Number of parallel jobs4`--exclude=PATH``-e`Exclude path from output (repeatable)-`--tests-dir=PATH`-Base test directory`./tests``--test-suite-prefix=PREFIX`-Test suite name prefix (generates part1, part2, ...)`part``--help``-h`Show help message-### Examples

[](#examples)

```
# Balance into 8 parallel jobs
vendor/bin/balance-phpunit-jobs -j 8 junit/*.xml

# With exclusions
vendor/bin/balance-phpunit-jobs \
  --jobs=4 \
  --exclude=./tests/Integration/Slow \
  --exclude=./tests/E2E \
  junit/*.xml

# Custom test suite prefix
vendor/bin/balance-phpunit-jobs -j 4 --test-suite-prefix=job junit/*.xml
# Generates: job1, job2, job3, job4
```

### Example Output

[](#example-output)

```

    ./tests/Unit/Service
    ./tests/Unit/SpecificTest.php

    ./tests/Integration
    ./tests/Unit/Repository

```

How It Works
------------

[](#how-it-works)

1. **Parse JUnit XML reports** - Extracts test file paths and their execution times from JUnit XML format (generated by PHPUnit with `--log-junit` option)
2. **Build timing tree** - Creates a hierarchical tree of test paths where each node accumulates the total execution time of all its children
3. **Greedy bin-packing with tree splitting** - The algorithm:

    - Calculates target time per job (total time / job count)
    - For each node, finds the job with minimum accumulated time
    - If adding the node keeps the job under target OR the node has no children, assigns it to that job
    - Otherwise, splits the node into its children for finer-grained distribution
4. **Generate PHPUnit XML** - Outputs testsuite fragments that can be used to configure parallel PHPUnit runs

Workflow
--------

[](#workflow)

The balancer uses historical timing data from JUnit XML reports generated by your CI runs. Since committing files from CI jobs is typically complex, the recommended workflow involves manually updating your `phpunit.xml`:

### Step 1: Configure CI to generate JUnit reports

[](#step-1-configure-ci-to-generate-junit-reports)

#### GitLab CI

[](#gitlab-ci)

```
test:
  parallel: 4
  script:
    - vendor/bin/phpunit --testsuite "part${CI_NODE_INDEX}" --log-junit junit.xml
  artifacts:
    paths:
      - junit.xml
    reports:
      junit: junit.xml
```

#### GitHub Actions

[](#github-actions)

```
jobs:
  test:
    strategy:
      matrix:
        part: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: vendor/bin/phpunit --testsuite "part${{ matrix.part }}" --log-junit junit.xml
      - name: Upload JUnit report
        uses: actions/upload-artifact@v4
        with:
          name: junit-part-${{ matrix.part }}
          path: junit.xml
```

### Step 2: Download JUnit reports and run the balancer

[](#step-2-download-junit-reports-and-run-the-balancer)

After your CI run completes, download the JUnit XML artifacts and run the balancer locally:

```
# Download artifacts from CI (e.g., into ./junit-reports/)
vendor/bin/balance-phpunit-jobs -j 4 ./junit-reports/*.xml
```

### Step 3: Update phpunit.xml

[](#step-3-update-phpunitxml)

Copy the generated testsuite fragments from the output and paste them into your `phpunit.xml`:

```

            ./tests/Unit/Service
            ./tests/Unit/SpecificTest.php

            ./tests/Integration
            ./tests/Unit/Repository

```

### Step 4: Commit and push

[](#step-4-commit-and-push)

Commit the updated `phpunit.xml` to your repository. Future CI runs will use the balanced test distribution.

### Re-balancing

[](#re-balancing)

Re-run this process periodically (e.g., when test execution times drift significantly) to keep the distribution optimal.

Comparison with Paratest
------------------------

[](#comparison-with-paratest)

[Paratest](https://github.com/paratestphp/paratest) and PHPUnit Parallel Job Balancer solve different problems and can be used together.

FeaturePHPUnit Parallel Job BalancerParatest**Purpose**Distributes tests across CI jobsRuns tests in parallel processes**Parallelization**Across machines/containersWithin a single machine**Timing-based balancing**Yes, uses historical JUnit XML dataLimited (WrapperRunner only)**CI integration**Native (GitLab CI, GitHub Actions, etc.)Requires single machine**Test isolation**Full (separate CI jobs)Process-level**Race conditions**None (complete isolation)Possible (shared resources)**Adoption effort**Minimal (no test changes needed)Often significant**Scalability**Scales with CI runnersLimited by CPU cores**Output**PHPUnit XML configurationDirect test execution### Why PHPUnit Parallel Job Balancer is easier to adopt

[](#why-phpunit-parallel-job-balancer-is-easier-to-adopt)

A common challenge with Paratest is that integration tests often need to be adjusted to be compatible with parallel execution. Tests that share resources (databases, files, caches, external services) can interfere with each other when running simultaneously, causing:

- **Race conditions** - Tests may randomly fail due to timing issues, and these bugs are notoriously hard to debug
- **Shared state conflicts** - Database records, file locks, or cache entries created by one test may affect another
- **Complex test refactoring** - Adapting an existing codebase with many integration tests can be very time-consuming

**PHPUnit Parallel Job Balancer avoids these issues entirely** because each CI job runs in complete isolation (separate container/machine). Your tests don't need any modifications - they run exactly as they would in a sequential PHPUnit execution, just distributed across multiple runners.

Requirements
------------

[](#requirements)

- PHP 8.1 or higher
- ext-dom
- ext-simplexml

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

[](#contributing)

- Check your code by `composer check`
- Autofix coding-style by `composer fix:cs`
- All functionality must be tested

License
-------

[](#license)

MIT License - see [LICENSE](LICENSE) file for details.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance75

Regular maintenance activity

Popularity25

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 80% 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

Unknown

Total

1

Last Release

166d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7a4170ebe9281cb76be91fe00f8eee307beb3e0744dfd40ba89d9c856372c7eb?d=identicon)[shipmonk](/maintainers/shipmonk)

---

Top Contributors

[![JanTvrdik](https://avatars.githubusercontent.com/u/175109?v=4)](https://github.com/JanTvrdik "JanTvrdik (4 commits)")[![janedbal](https://avatars.githubusercontent.com/u/1993453?v=4)](https://github.com/janedbal "janedbal (1 commits)")

---

Tags

balancerciparallelphpunitteststestingphpunitxmlparallelcijunitjob-balancertest-distribution

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/shipmonk-phpunit-parallel-job-balancer/health.svg)

```
[![Health](https://phpackages.com/badges/shipmonk-phpunit-parallel-job-balancer/health.svg)](https://phpackages.com/packages/shipmonk-phpunit-parallel-job-balancer)
```

###  Alternatives

[brianium/paratest

Parallel testing for PHP

2.5k118.8M754](/packages/brianium-paratest)[rregeer/phpunit-coverage-check

Check the code coverage using the clover report of phpunit

606.1M179](/packages/rregeer-phpunit-coverage-check)[facile-it/paraunit

paraunit

146721.6k11](/packages/facile-it-paraunit)[moodlehq/moodle-plugin-ci

Helps running Moodle plugins analysis checks and tests under various CI environments.

612.6M](/packages/moodlehq-moodle-plugin-ci)[lmc/steward

Steward - makes Selenium WebDriver + PHPUnit testing easy and robust

222163.1k1](/packages/lmc-steward)[acquia/orca

A tool for testing a company's software packages together in the context of a realistic, functioning, best practices Drupal build

32902.4k](/packages/acquia-orca)

PHPackages © 2026

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