-
Notifications
You must be signed in to change notification settings - Fork 0
Testing
Two angles here: how the package itself is tested, and how to test your code that uses the loop.
FiberLoops ships a PHPUnit suite with 100% line coverage of src, plus
PHPStan (level 8) and PHP-CS-Fixer (PSR-12). Composer exposes the usual scripts:
composer install
composer test # run PHPUnit
composer stan # run PHPStan
composer cs-check # check PSR-12 (no changes)
composer cs-fix # apply PSR-12 fixes
composer ci # cs-check + stan + test, the full local gateCI runs the same gate on PHP 8.1, 8.2, 8.3 and 8.4 (plus a lowest-dependency leg) on every push and pull request.
The loop is fully deterministic: with cooperative scheduling, the order in which tasks interleave is fixed and reproducible — there is no timing or thread-race to flake on. That makes loop-based code pleasant to test.
The cleanest approach is to have your tasks append to a shared array and assert the final sequence. No output buffering, no timing:
use InitPHP\FiberLoops\Loop;
use PHPUnit\Framework\TestCase;
final class InterleavingTest extends TestCase
{
public function test_two_tasks_take_turns(): void
{
$log = [];
$loop = new Loop();
$loop->defer(function () use ($loop, &$log) {
foreach (['a1', 'a2'] as $s) {
$log[] = $s;
$loop->next();
}
});
$loop->defer(function () use ($loop, &$log) {
foreach (['b1', 'b2'] as $s) {
$log[] = $s;
$loop->next();
}
});
$loop->run();
$this->assertSame(['a1', 'b1', 'a2', 'b2'], $log);
}
}For code that uses await(), assert the value directly:
public function test_await_returns_the_value(): void
{
$loop = new Loop();
$result = $loop->await(function () use ($loop) {
$loop->next();
return 42;
});
$this->assertSame(42, $result);
}LoopException is part of the contract, so it is worth asserting too:
use InitPHP\FiberLoops\Exception\LoopException;
public function test_next_outside_a_fiber_throws(): void
{
$this->expectException(LoopException::class);
(new Loop())->next();
}If a class of yours takes a loop, type-hint the interface so a test can pass a double or an alternative scheduler:
use InitPHP\FiberLoops\LoopInterface;
final class JobRunner
{
public function __construct(private LoopInterface $loop) {}
public function run(): void
{
$this->loop->defer(fn () => /* ... */);
$this->loop->run();
}
}
// In a test, pass a real Loop (it is lightweight and deterministic):
$runner = new JobRunner(new \InitPHP\FiberLoops\Loop());In most cases the real Loop is the best "double" — it is tiny, has no I/O, and
runs deterministically, so there is rarely a reason to mock it.
Avoid asserting exact durations around sleep(). microtime() has microsecond
granularity and subtracting two large timestamps loses a little floating-point
precision, so a pause that is genuinely ≥ 0.05s can measure microscopically under
it. If you must assert a duration, use a generous tolerance (e.g. "at least 0.045s
for a 0.05s sleep") rather than an exact value.
- Error Handling — the contract worth asserting.
- API Reference — the surface you are testing against.
Getting Started
Using FiberLoops
Guides
Reference