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/greenlightRunning
Greenlight dev-mainPHP 8.4.14 · configuration: greenlight.php · workers: 11

38 / 802 tests11 workers

  1. Acceptance\WorkerProcessRunTest43.302s
  2. Acceptance\CliTest53.302s
  3. Acceptance\PhpStanMatcherSignatureTest03.297s
  4. Acceptance\SymfonyRunTest00.762s

… and 3 more running

Use every available coreThe 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. Greenlight finds each #[Test] method without a base class or method-name convention.

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::that($total->format())->toBe($expected);
    }
}

Run the suite

Greenlight manages the worker pool for you.

  1. 01

    Run one command

    Greenlight discovers the suite once and builds the execution plan.

  2. 02

    Keep every worker busy

    The orchestrator assigns one scheduling unit to each available worker. Greenlight starts known slow classes and previously failed classes early.

  3. 03

    Read one clear result

    Greenlight checks every worker result and prints one ordered 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 testA hard timeout stops the worker. 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 dev-mainPHP 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::that()->toBe()
createMock()
injected strict Doubles

Try one test class

Go from installation to your first run.

Before you change the rest of your suite, run one test class successfully.

  1. 01

    Install Greenlight

    composer require --dev greenlight/greenlight
  2. 02

    Add greenlight.php

    <?php return GreenlightConfig::create();
  3. 03

    Run one class

    vendor/bin/greenlight run --filter=PriceTest