giorgiosironi/eris
Eris brings QuickCheck-style property-based testing to PHP and PHPUnit. Define properties, generate many random inputs, and find minimal counterexamples automatically. Works with PHP 8.1–8.4 and PHPUnit 10–13.
Installation:
composer require --dev giorgiosironi/eris
Ensure your phpunit.xml supports PHPUnit 10.x–13.x and PHP 8.1–8.4.
First Test:
Create a test class extending PHPUnit\Framework\TestCase and use the TestTrait:
use Eris\Generators;
use Eris\TestTrait;
class MyTest extends \PHPUnit\Framework\TestCase
{
use TestTrait;
public function testExample()
{
$this->forAll(Generators::nat()) // Natural numbers
->then(fn(int $n) => $this->assertGreaterThan(0, $n));
}
}
Run Tests:
vendor/bin/phpunit --testdox
Define Generators:
Use Generators::* static methods (e.g., Generators::string(), Generators::array()).
$generator = Generators::tuple(
Generators::int(),
Generators::string(Generators::printableCharacter())
);
Compose Properties:
Chain generators with forAll() and assertions:
$this->forAll($generator)
->then(fn([int $a, string $b]) => $this->assertTrue(strlen($b) > 0));
Custom Constraints:
Use suchThat() to filter inputs:
$this->forAll(Generators::int()->suchThat(fn(int $n) => $n % 2 === 0))
->then(fn(int $n) => $this->assertTrue($n % 2 === 0));
PHPUnit Annotations: Control test behavior via annotations:
/**
* @eris-repeat 100
* @eris-duration 5s
*/
public function testPerformance() { ... }
Listeners: Hook into test execution for logging or metrics:
$this->hook(Listener\collectFrequencies('output.json'));
Shrinking: Enable deterministic shrinking for minimal failing inputs:
$this->forAll($generator)->shrink()->then(...);
| Use Case | Generator Example | Test Example |
|---|---|---|
| Random Strings | Generators::string(Generators::char()) |
$this->assertTrue(is_string($s)); |
| Nested Structures | Generators::array(Generators::int()) |
$this->assertCount(10, $arr); |
| Date/Time | Generators::date('2020-01-01', '+1 day') |
$this->assertInstanceOf(DateTime::class, $d); |
| Regex Matching | Generators::regex('/^[A-Za-z]+$/') |
$this->assertMatchesRegularExpression(...); |
Shrinking Behavior:
->shrink() explicitly and inspect outputs with ERIS_ORIGINAL_INPUT=1.Generator Size:
Generators::string()->size(1000)) can slow tests.->limitTo(100) for performance.PHPUnit 10+:
@eris-method may conflict with PHPUnit’s new attributes.->hook(Listener\...) instead of annotations.Floating-Point Precision:
Generators::float()) may produce edge cases (e.g., NaN).->suchThat(fn(float $f) => is_finite($f)).Deterministic Seeds:
ERIS_SEED=42 for reproducible runs, but avoid in CI to catch edge cases.Inspect Generated Values:
$this->forAll($generator)
->then(fn($value) => $this->assertTrue(/* ... */))
->hook(Listener\log('debug.log'));
Custom Shrinkers: Override shrinking for complex types:
Generators::custom(
fn(GeneratedValueOptions $options) => new MyType(...),
fn(MyType $value) => [$value->getSimplerRepresentation()]
);
Performance:
->ratio(0.1) to reduce test iterations for quick feedback.->sample(100)->shrink().Custom Generators:
Implement GeneratorInterface for domain-specific types:
class UserGenerator implements GeneratorInterface {
public function generate(GeneratedValueOptions $options): GeneratedValue {
return new GeneratedValueSingle(new User(...));
}
}
Listeners:
Extend ListenerInterface to log or analyze test runs:
class MyListener implements ListenerInterface {
public function onAttempt(Attempt $attempt) {
// Custom logic
}
}
Annotations:
For PHPUnit <10, use TestTrait annotations. For PHPUnit 10+, migrate to ->hook() or attributes.
Randomness:
Defaults to mt_rand(). Override globally:
Eris\Facade::setRandomness('rand');
Shrinking Limits:
Disable with ->disableShrinking() or set a time limit:
$this->forAll($generator)->shrink()->timeLimit(1000);
PHPUnit Integration:
Ensure TestTrait is used in test classes, not traits or base classes.
How can I help you explore Laravel packages today?