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

Paratest Laravel Package

brianium/paratest

ParaTest brings zero-configuration parallel testing to PHPUnit. Run tests across multiple processes by TestCase or Test, and combine code coverage into one report. Install via Composer and use vendor/bin/paratest with no extra bootstrap needed.

View on GitHub
Deep Wiki
Context7

Getting Started

ParaTest is the parallel test runner that Laravel’s own php artisan test --parallel command is built on. If your Laravel suite spends most of its time in feature tests that boot the framework and hit the database, this is the first lever to pull.

Start here:

  1. Check whether ParaTest is already installed:

    composer show brianium/paratest
    

    If not, install it as a dev dependency:

    composer require --dev brianium/paratest
    

    Before committing to a version, check compatibility with your pinned phpunit/phpunit and Symfony versions. ParaTest intentionally tracks only the latest PHPUnit, so Laravel 9/10 projects may need an older ParaTest major or a pinned minor. Use composer why-not brianium/paratest and the release notes when resolving conflicts.

  2. Try Laravel’s wrapper first:

    php artisan test --parallel
    php artisan test --parallel --processes=4
    

    Laravel will create per-process test databases and set TEST_TOKEN for each worker. That is usually enough for a standard Laravel app.

  3. If you need direct control, use the binary:

    vendor/bin/paratest --help
    vendor/bin/paratest --processes=4
    
  4. First use case: take a slow MySQL-backed feature suite and run php artisan test --parallel --processes=4 --recreate-databases. If it works, benchmark with 4 vs 8 processes. If tests collide over the database, move to token-aware configuration described below.

Where to look first in the package docs: Test token, Initial setup for all tests, Troubleshooting, and Caveats in the README. Also run vendor/bin/paratest --help; ParaTest forwards many PHPUnit options, but not all, and coverage/reporting options have been added incrementally.

Implementation Patterns

Use Laravel’s parallel runner as the default path. For most Laravel teams, php artisan test --parallel is the day-to-day command. It handles database creation, migrations, and token injection. Useful options:

php artisan test --parallel --processes=8
php artisan test --parallel --recreate-databases
php artisan test --parallel --coverage --min=80

Customize per-process database setup with ParallelTesting. Laravel exposes hooks in tests/TestCase.php:

namespace Tests;

use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Illuminate\Support\Facades\ParallelTesting;

abstract class TestCase extends BaseTestCase
{
    protected function setUp(): void
    {
        parent::setUp();

        ParallelTesting::setUpTestDatabase(function (string $database, int $token) {
            // Seed shared reference data, configure extensions, etc.
        });

        ParallelTesting::setUpProcess(function (int $token) {
            // Per-process setup: cache prefix, storage path, etc.
        });
    }
}

Use this for shared seed data or per-process external resource namespacing. Do not use it for logic that must happen exactly once for the whole run; it runs per process.

Configure token-aware connections when calling ParaTest directly. If you bypass php artisan test --parallel, each worker needs its own database. Use TEST_TOKEN:

// config/database.php
'testing' => [
    'driver' => 'mysql',
    'database' => 'testdb_'.(getenv('TEST_TOKEN') ?: '0'),
    // ...
],

For SQLite file databases, use a unique file per process:

'database' => database_path('testing_'.(getenv('TEST_TOKEN') ?: '0').'.sqlite'),

Or keep using :memory: SQLite, which is naturally isolated per process. For MySQL/Postgres, create the token databases before the run, or use Laravel’s parallel runner, which does it for you.

Namespace external services per process. Tests that share Redis, file cache, queues, or object storage will interfere under parallel execution. In phpunit.xml prefer process-local drivers:

<env name="CACHE_STORE" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>

If you must use Redis, prefix keys with TEST_TOKEN or, better, UNIQUE_TEST_TOKEN:

'prefix' => 'laravel_'.(getenv('UNIQUE_TEST_TOKEN') ?: 'local'),

TEST_TOKEN is distinct per running process; UNIQUE_TEST_TOKEN is unique per run and process. Use the latter for external resources that persist across CI runs.

Coverage merging is a strong reason to use ParaTest. With Xdebug:

XDEBUG_MODE=coverage php artisan test --parallel --coverage --min=80

With PCOV, enable it in child processes via --passthru-php:

php -d pcov.enabled=1 vendor/bin/paratest \
  --passthru-php="'-d' 'pcov.enabled=1'" \
  --coverage-clover build/coverage.xml

ParaTest combines coverage from workers into one report. Recent versions also support --coverage-openclover, --coverage-text, and --only-summary-for-coverage-text.

Use --functional selectively. By default ParaTest parallelizes by test class/file. --functional parallelizes by test method, which helps when a handful of large test classes dominate the suite. The trade-off is more process overhead and weaker support for @depends and class-level state. Prefer the default mode for normal Laravel feature suites.

Shard in CI when one machine is not enough. Recent ParaTest versions support --shard:

vendor/bin/paratest --shard=1/4 --processes=4 --log-junit junit-1.xml

Combine shard reports in CI. This is useful for large suites where even 8–16 local processes are not enough.

PHPStorm integration. Use the dedicated helper binary in the PHPUnit run configuration’s interpreter options:

./vendor/bin/paratest_for_phpstorm

Add ParaTest options under “Test runner options”. Ensure PHPUnit and coverage are already configured in PHPStorm; the helper relies on that setup.

Pest projects. Pest’s --parallel support also uses ParaTest:

./vendor/bin/pest --parallel

The same TEST_TOKEN, database, and external-service isolation rules apply.

Debug failing workers. Run with --verbose. ParaTest prints the originating worker command when a subprocess fails. Copy that command, remove the --printer ...NullPhpunitPrinter option, and rerun it to see PHPUnit’s normal output. This is the fastest way to debug worker-only failures.

Gotchas and Tips

  • Version alignment is the biggest trap. ParaTest only supports the latest PHPUnit, and recent releases drop older Symfony versions. For example, ParaTest 7.9+ moves to PHPUnit 12, and 7.15 drops Symfony 6. Laravel 10 projects on Symfony 6.2 / PHPUnit 10 should not blindly install the newest ParaTest. Check composer why-not, pin a compatible minor, and avoid composer update brianium/paratest in isolation. Laravel 9 projects may need ParaTest v6 rather than v7.
  • Static initialization is per process, not per run. A private static bool $initialized flag runs once per worker. If you need exactly-once initialization for the entire invocation, use a filesystem lock (flock) as shown in the README, or move the work into a CI/pre-test step. Laravel’s ParallelTesting::setUpProcess is per process, not per run.
  • Shared databases cause flaky collisions. RefreshDatabase/DatabaseTransactions against one shared MySQL database will deadlock or leak state across workers. Use Laravel’s parallel database creation, token-suffixed databases, or :memory: SQLite. File-based SQLite shared across processes is a common source of “database is locked” errors.
  • TEST_TOKEN is not a run identifier. It only guarantees uniqueness among currently running processes. Use UNIQUE_TEST_TOKEN for external namespaces, S3 buckets, Redis prefixes, and anything that could collide with a previous CI run.
  • Test classes cannot safely expose shared constants, static methods, static variables, or reflection targets to other test classes. This is an explicit ParaTest caveat. Move shared helpers into normal classes outside the test suite.
  • --functional breaks more than it fixes in some suites. It runs each test method in a separate process, so @depends, setUpBeforeClass, and class-level assumptions may not behave as expected. Also check your ParaTest version for JUnit merging fixes in functional mode (fixed in 7.11.1).
  • Coverage engines need explicit enablement in workers. Xdebug requires XDEBUG_MODE=coverage in the environment. PCOV often needs --passthru-php so child PHP processes enable it. Quoting for --passthru-php is shell-sensitive; verify locally and in CI.
  • Subprocess output is intentionally silenced. ParaTest runs workers with a null PHPUnit printer, so output can look sparse compared with a normal PHPUnit run. Use --verbose and rerun the printed worker command without the null printer when debugging.
  • Process count should match CI resources. The default is based on CPU cores; in constrained containers, set --processes explicitly. More processes can slow you down when each Laravel worker boots, hits the database, and consumes memory.
  • Not every PHPUnit option is passed through. Check vendor/bin/paratest --help for the supported set. Coverage and TestDox options have been added over time (--testdox-text/--testdox-html in 7.14.0, time-limit options in 7.10.0), so older pinned versions may silently lack them.
  • Laravel’s wrapper hides some ParaTest options. php artisan test --parallel exposes --processes, --recreate-databases, and coverage/minimum options, but if you need sharding or specific ParaTest flags, call vendor/bin/paratest directly.
  • Windows + PHPStorm had helper-binary issues fixed in 7.11.2; use that or newer if you run PHPStorm on Windows.
  • Worker crashes can be silent in older versions. ParaTest 7.16.1 added detection for workers that crash after test execution but before writing result files. If you see missing results or truncated reports, upgrade before debugging your own suite.
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.
thuraaung2493/laravel-rulecraft
cyberwizard/schedule-catchup
livetennisapi/livetennisapi-laravel
roundly-consulting/notifications-for-laravel
flightsadmin/bootstrap-starter-kit
lasserafn/laravel-intempus
likyly/sdk
tdt/input
coreation/php-shapefile
coreation/gisconverter
primix/widgets
primix/notifications
primix/actions
primix/tables
primix/details
primix/forms
primix/support
marcelgwerder/laravel-api-handler
firebrikdotcom/conveyor-laravel-broadcaster
kanata-php/conveyor-laravel-broadcaster