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

Paraunit Laravel Package

facile-it/paraunit

Paraunit speeds up PHPUnit test suites by running tests in parallel across multiple PHPUnit processes. Built on Symfony components, it can collect coverage in parallel and supports many PHPUnit and Symfony versions. Install it via Composer as a dev dependency.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Check compatibility first. Paraunit 2.x requires PHPUnit 10.5.4+ (newer 2.x releases support PHPUnit 11, 12, and 13) and Symfony 4.4–8. Laravel 10/11/12 projects are generally in range; Laravel 9 projects on PHPUnit 9 are not.

  2. Install as a dev dependency:

composer require --dev facile-it/paraunit
  1. Run it once against your existing PHPUnit config:
vendor/bin/paraunit run

Paraunit reads your phpunit.xml test suites and launches one PHPUnit process per test class. On the first run with Paraunit 2.x, it will detect that its PHPUnit bootstrap extension is missing and ask to add it to phpunit.xml. Accept that change and commit phpunit.xml—otherwise CI will keep prompting or fail to integrate correctly.

  1. Start with the fastest useful workflow: run only your unit suite while developing, then run everything before pushing:
vendor/bin/paraunit run --testsuite=Unit --stop-on-failure
vendor/bin/paraunit run
  1. Where to look first: your phpunit.xml test suites, then the Paraunit documentation. The main day-to-day payoff is replacing vendor/bin/phpunit with vendor/bin/paraunit run for large suites, plus parallel coverage in CI.

Implementation Patterns

Composer scripts as the team entry point

Expose parallel execution consistently rather than relying on developers to remember the binary:

{
  "scripts": {
    "test": "vendor/bin/paraunit run",
    "test:unit": "vendor/bin/paraunit run --testsuite=Unit",
    "test:feature": "vendor/bin/paraunit run --testsuite=Feature",
    "test:coverage": "vendor/bin/paraunit coverage --html=build/coverage"
  }
}

Then CI uses the same commands as local development.

Split fast and slow suites

Paraunit respects PHPUnit test suites, so map your Laravel Unit and Feature suites to separate commands:

vendor/bin/paraunit run --testsuite=Unit
vendor/bin/paraunit run --testsuite=Feature

Use --exclude-testsuite=Feature when you want everything except database-heavy tests. This is useful for pre-commit hooks where a full feature suite is too slow.

Handle database isolation explicitly

Paraunit parallelizes test classes across separate PHPUnit processes. It does not provide Laravel’s php artisan test --parallel database lifecycle hooks, so you must make each process safe.

Safest first step: keep database-bound tests serial, and only parallelize unit tests:

vendor/bin/paraunit run --testsuite=Unit
vendor/bin/phpunit --testsuite=Feature

If your feature tests use SQLite in-memory, parallel execution is usually straightforward because each process has its own in-memory database. For MySQL/PostgreSQL, use one database per worker process. A common pattern is to derive the database name from the process ID in a base TestCase:

protected function setUp(): void
{
    parent::setUp();

    $database = 'test_' . getmypid();

    config([
        'database.connections.mysql.database' => $database,
    ]);

    DB::purge('mysql');
}

Pre-create the databases in CI, or have each process create its own before RefreshDatabase runs. Avoid shared database names, shared Redis keys, and shared search indexes unless the tests are written to tolerate concurrency.

Use --stop-on-* for red/green loops

Paraunit 2.11 exposes PHPUnit-style stop options:

vendor/bin/paraunit run --stop-on-failure
vendor/bin/paraunit run --stop-on-error --stop-on-defect

These affect both Paraunit and the underlying PHPUnit processes, so you get a fast failure signal instead of waiting for the entire suite.

Tune process overhead with --chunk-size

If you have many tiny test classes, launching one process per class can add noticeable overhead. Group work with:

vendor/bin/paraunit run --chunk-size=10

Larger chunks reduce process startup cost but also reduce parallelism. Tune this per suite; it is usually most useful for large numbers of very small unit test classes.

Detect order dependencies with random sorting

vendor/bin/paraunit run --sort=random

Run this periodically in CI or locally to catch tests that only pass because of execution order. It is a cheap smoke test for hidden shared state.

Pass through PHPUnit options

When Paraunit does not mirror a PHPUnit option you need, use --pass-through:

vendor/bin/paraunit run --pass-through="--filter=SomeSpecificTest"

Check vendor/bin/paraunit run --help for the exact quoting and supported syntax in your installed version, because pass-through arguments are forwarded to child PHPUnit processes.

Parallel coverage in CI

Paraunit selects the best available coverage driver automatically: PCOV, then Xdebug, then PHPDbg. Prefer PCOV in CI for speed.

vendor/bin/paraunit coverage --html=build/coverage
vendor/bin/paraunit coverage --clover=build/coverage.xml
vendor/bin/paraunit coverage --cobertura=build/cobertura.xml

Cobertura is useful for GitLab coverage visualization. Coverage output formats mirror PHPUnit’s: HTML, Clover, Cobertura, XML, Crap4j, PHP, text, and console text.

Use debugging by isolating the failing class

Because Paraunit splits by test class, a failure normally maps cleanly to one class. Reproduce with plain PHPUnit first:

vendor/bin/phpunit --filter=TheFailingTestClass

Then return to Paraunit to confirm the parallel behavior. This avoids conflating a real test failure with a parallelism or environment issue.

Gotchas and Tips

  • PHPUnit version compatibility is strict. Paraunit 2.x does not support PHPUnit 9. If you are on Laravel 9, you cannot simply drop Paraunit in; plan the PHPUnit/Laravel upgrade first. Check the version matrix before upgrading Laravel or PHPUnit.

  • The bootstrap extension must be committed. Paraunit 2.x integrates through PHPUnit’s event system and needs an extension registered in phpunit.xml. On first run it offers to add it automatically. Run once locally, commit the XML change, and verify CI does not run in a non-interactive mode that prevents the prompt from being handled.

  • Parallelism is per test class, not per test method. One enormous test class will remain a bottleneck. Split large classes or use --chunk-size carefully; chunking groups tests into fewer processes, which reduces overhead but also reduces parallelism.

  • Laravel’s ParallelTesting hooks are not used. Paraunit is not php artisan test --parallel. Features such as ParallelTesting::setUpProcess() and Laravel’s database recreation workflow do not apply. Treat each Paraunit worker as a normal standalone PHPUnit process and isolate resources yourself.

  • Shared external state is the main source of flakiness. Databases, Redis, queues, filesystem paths, object storage, and search indexes can collide across processes. Prefer per-process databases and per-process cache prefixes. If a test writes to a fixed path under storage/, make it process-specific or serialize that suite.

  • Cached Laravel config can defeat per-process environment overrides. If config:cache has been run, changing environment variables inside a worker may not behave as expected. Prefer runtime config([...]) overrides in setUp() or ensure CI runs without a stale cached config.

  • Deprecation output can appear hidden. Since Paraunit 2.6, deprecation details follow PHPUnit’s displayDetailsOnTestsThatTriggerDeprecations setting, which defaults to false. To see them, enable that attribute or use the mirrored CLI flags such as --display-deprecations and --display-all-issues.

  • --check-php-configuration is rejected. Paraunit 2.7+ intentionally rejects this PHPUnit option because it does not execute tests. Do not include it in your Paraunit CI command.

  • Coverage drivers matter. Xdebug coverage is significantly slower than PCOV. Install PCOV in CI where possible. Paraunit can enable Xdebug coverage mode automatically, but that does not make Xdebug as fast as PCOV.

  • Pest is not explicitly documented. Paraunit integrates with PHPUnit’s event system, and Pest runs on top of PHPUnit, but verify compatibility with your Pest/PHPUnit versions before standardizing on it. If you hit issues, keep Pest on plain PHPUnit or use a Pest-aware parallel runner until you have validated Paraunit in your project.

  • Watch exit codes in CI. Paraunit aggregates child process results. Make sure your CI step fails on Paraunit’s non-zero exit code and does not swallow output. Run the exact CI command locally once to confirm failures propagate correctly.

  • Use --test-suffix and --exclude-testsuite for nonstandard layouts. If your repository uses custom test suffixes or generated test directories, these options avoid accidentally running or skipping suites. Confirm with --help in your installed version.

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.
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
adachsoft/changelog-linter