giorgiosironi/eris
Eris brings QuickCheck-style property-based testing to PHP and PHPUnit. Define properties, generate many randomized inputs, and let Eris shrink failing cases to minimal counterexamples. Works with PHP 8.1+ and PHPUnit 10–13 via a simple TestTrait.
composer require --dev giorgiosironi/eris
TestTrait in your PHPUnit test classes:
use Eris\TestTrait;
use Eris\Generators;
class ExampleTest extends \Tests\TestCase
{
use TestTrait;
}
Test a simple property (e.g., "sum of two numbers is commutative"):
public function testSumCommutativity()
{
$this->forAll(
Generators::int(),
Generators::int()
)
->then(function ($a, $b) {
$this->assertEquals($a + $b, $b + $a);
});
}
Generators::* static methods (e.g., int(), string(), date()).@eris-* annotations for test configuration (e.g., @eris-repeat 100).Generators::* to create input generators.
$generator = Generators::array(Generators::string(), 10);
forAll: Bind generators to test logic.
$this->forAll($generator)
->then(function ($array) {
$this->assertCount(10, $array);
});
Generators::tuple(), Generators::oneOf(), or Generators::suchThat().
$this->forAll(
Generators::tuple(Generators::int(), Generators::string())
)->then(function ($tuple) {
$this->assertIsInt($tuple[0]);
});
TestCase and include TestTrait:
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
use Eris\TestTrait;
class FeatureTest extends BaseTestCase
{
use TestTrait;
}
Generators::date() or Generators::uuid() for realistic test data:
$this->forAll(Generators::date('Y-m-d'))
->then(function ($date) {
$this->assertDatabaseHas('events', ['date' => $date]);
});
Eris\Generator\AbstractGenerator for domain-specific inputs.$this->forAll(Generators::int())
->shrink()
->then(...);
Listener::* (e.g., log generations or collect frequencies):
$this->hook(Listener\collectFrequencies('generations.log'));
Generators::string(1000)) may slow tests. Use limitTo() to cap iterations:
$this->forAll(Generators::string(1000))
->limitTo(100)
->then(...);
$this->forAll(Generators::int())
->disableShrinking()
->then(...);
@eris-* annotations don’t clash with Laravel’s testing annotations (e.g., @test).ERIS_SEED=123 for reproducible test runs.ERIS_ORIGINAL_INPUT=1 to log generated inputs in failures.Listener\log() to debug generator behavior:
$this->hook(Listener\log('debug.log'));
Generators::oneOf() with complex types (see #153).Eris\Shrinker\ShrinkerInterface for domain-specific shrinking logic.Generators::bind() to create dependent generators:
$generator = Generators::bind(
Generators::int(),
function ($size) { return Generators::array(Generators::string(), $size); }
);
Eris\Listener\ListenerInterface to integrate with Laravel’s event system (e.g., log test results to Scout).$this->app->bind('custom.generator', function () {
return new CustomGenerator();
});
Generators::uuid() for unique test data in integration tests:
$this->forAll(Generators::uuid())
->then(function ($uuid) {
$this->assertDatabaseMissing('users', ['id' => $uuid]);
});
$cachedGenerator = Generators::memoize(
Generators::complexGenerator(),
fn ($value) => Cache::remember("generator_{$value}", now()->addHour(), fn () => $value)
);
parallel PHPUnit flag for faster property checks.How can I help you explore Laravel packages today?