A parallel-first testing framework for PHP.

Greenlight discovers your suite once and schedules its test classes across a worker pool for parallel execution by default.

composer require --dev greenlight/greenlight
vendor/bin/greenlightExample run
Greenlight 0.3.0PHP 8.4.14 · configuration: greenlight.php · workers: 11

38 / 802 tests11 workers

Test classes in progress
Test classTestsElapsed
Acceptance\WorkerProcessRunTest43.302s
Acceptance\CliTest53.302s
Acceptance\PhpStanMatcherSignatureTest03.297s
Acceptance\SymfonyRunTest00.762s

… and 3 more running

Use parallel workersThe runner executes test classes in parallel.

Use plain PHP testsTests need no base class or method-name convention.

Use your current toolsTyped APIs work with your IDE and PHPStan.

Write a test

Tests stay plain PHP.

Use ordinary classes, native attributes, and typed expectations. This example uses a Price class from the application under test.

Write your first test
<?php

use Greenlight\Attribute\DataRow;
use Greenlight\Attribute\Test;
use Greenlight\Expect\Expect;

final class PriceTest
{
    #[Test]
    #[DataRow(['9.99', 2, '19.98'])]
    public function multipliesLineTotals(
        string $unit,
        int $quantity,
        string $expected,
    ): void {
        $total = Price::fromString($unit)->times($quantity);

        Expect::value($total->format())->toBe($expected);
    }
}
Execution planWorkers execute test classes. One report combines their results.

Greenlight discovers the suite once, assigns test classes to available workers, and combines their results into one report.

Run the suite

Greenlight manages the worker pool for you.

The runner discovers the suite once, assigns work as resources become available, and combines the results in one report.

  1. 01

    Run one command

    Greenlight discovers the suite once and builds the execution plan.

  2. 02

    Assign work to available workers

    The orchestrator assigns one scheduling unit to each available worker. Without randomized order, saved run state puts previously failed classes first and known slow classes next.

  3. 03

    Read one clear result

    Greenlight checks every worker result and combines the results in one report.

Test failures

Failures stay contained and readable.

Crashed workerThe orchestrator returns the rest of the assignment to the queue and starts a replacement worker.

Hung testSet a test timeout to stop a blocked worker after the timeout grace period. A replacement worker continues the suite.

External resourcesUse channels to select separate resources for each worker. Resource limits restrict concurrent access to shared dependencies.

Missed mock callWhen the test ends, Greenlight verifies strict doubles and reports unmet expectations.

greenlightrun complete
 vendor/bin/greenlightGreenlight 0.3.0PHP 8.4.14 | configuration: greenlight.php | workers: 11 | seed: 560647545 Unit\Coverage\XdebugDriverTest (1 test, skipped, 0.002s)802 tests, 801 passed, 1 skipped, 2797 expectationsTime: 10.341sWorkers: 11 spawnedSkipped:  XdebugDriverTest::collectsRealLineCoverageOverTheFixture  (xdebug with coverage mode is not available)Slowest tests:  4.187s PhpStanToThrowRuleTest::patternAndExactMessageConstraints…  4.174s PhpStanMatcherSignatureTest::reflectedMatcherSignatures…  3.908s PhpStanDataProviderShapeRuleTest::providerAndRowShapes…

Move from PHPUnit

Move one test class at a time.

Greenlight changes the test support code but does not change the code under test. Start with one small test class that has few dependencies. Run it with one worker. After it passes, enable parallel execution.

Read the PHPUnit migration guide
extends TestCase
plain class
test*()
#[Test] on a public method
$this->assertSame()
Expect::value()->toBe()
createMock()
injected strict Doubles

Run Greenlight

Run your tests in parallel.

Start with a complete configuration and one small test class, or move an existing PHPUnit class when you are ready.