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 run38 / 802 tests11 workers
| Test class | Tests | Elapsed |
|---|---|---|
Acceptance\WorkerProcessRunTest | 4 | 3.302s |
Acceptance\CliTest | 5 | 3.302s |
Acceptance\PhpStanMatcherSignatureTest | 0 | 3.297s |
Acceptance\SymfonyRunTest | 0 | 0.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.
<?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);
}
}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.
- 01
Run one command
Greenlight discovers the suite once and builds the execution plan.
- 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.
- 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.
❯ 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 guideextends 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.