Installation
composer require --dev eventsauce/test-utilities
Ensure eventsauce/eventsauce (v2.0 or v3.0) and PHPUnit (v8.5+) are installed.
First Use Case: Testing Event Sourcing
Use the EventSauce\TestUtilities\TestUtil trait in your PHPUnit test class to simplify event sourcing assertions:
use EventSauce\TestUtilities\TestUtil;
class MyAggregateTest extends TestCase
{
use TestUtil;
public function testAggregateBehavior()
{
$aggregate = new MyAggregate();
$this->assertEvents($aggregate, [new MyEvent()]);
}
}
Key Entry Points
assertEvents(): Verify emitted events.assertEvent(): Check for a single event.assertNoEvents(): Ensure no events were emitted.assertEventOrder(): Validate event sequence.Refer to EventSauce Testing Docs for full API.
Setup
Use TestUtil to mock event storage and simplify assertions:
use EventSauce\TestUtilities\TestUtil;
class OrderAggregateTest extends TestCase
{
use TestUtil;
public function testOrderCreation()
{
$order = new OrderAggregate();
$order->create('user-123', 100.00);
$this->assertEvents($order, [
new OrderCreated('user-123', 100.00),
new OrderInitialized()
]);
}
}
Integration with EventSauce
EventSauce\EventSourcing\EventSourcingRepository for real storage tests.TestUtil::assertEvent() to validate event properties:
$this->assertEvent($order, OrderCreated::class, function ($event) {
$this->assertEquals(100.00, $event->amount());
});
Testing Projections
EventSauce\Projection\Projection to test read models:
$projection = new OrderProjection();
$this->runProjection($projection, [
new OrderCreated('user-123', 100.00)
]);
$this->assertEquals(100.00, $projection->getTotal());
Mocking External Services
TestUtil::mock() to stub dependencies (e.g., payment gateways) during event tests:
$this->mock(PaymentGateway::class, function ($mock) {
$mock->shouldReceive('charge')->once();
});
Event Matching Strictness
assertEvents() uses strict type checking by default. Use true as the 3rd argument to relax matching:
$this->assertEvents($aggregate, [new MyEvent()], true); // Loose matching
false) to catch typos in event classes.Event Order Sensitivity
assertEventOrder() is order-sensitive. Reorder events in the test if the actual sequence differs from expectations.PHPUnit Version Conflicts
composer.json constraints (e.g., ^9.4). Use:
composer require --dev phpunit/phpunit:^9.5
Aggregate Hydration Issues
TestUtil is used after hydration:
// ❌ Wrong: TestUtil won't track events emitted during hydration.
$aggregate = $repository->hydrate($id);
// ✅ Correct: Use TestUtil for new interactions.
$this->assertEvents($aggregate, []);
$aggregate->doSomething();
Event Dumping
Use TestUtil::getRecordedEvents() to inspect emitted events:
var_dump($this->getRecordedEvents($aggregate));
Projection Debugging For failing projections, log intermediate state:
$this->runProjection($projection, $events, function ($projection) {
error_log('Projection state: ' . print_r($projection->getState(), true));
});
Custom Assertions
Extend TestUtil for domain-specific checks:
protected function assertCustomEvent($aggregate, $expected)
{
$this->assertEvent($aggregate, CustomEvent::class, function ($event) use ($expected) {
$this->assertEquals($expected->value(), $event->value());
});
}
Custom Event Matchers
Override TestUtil::matches() to support custom event logic:
protected function matches($event, $expected, $strict = false)
{
if ($expected instanceof CustomEvent) {
return $event->id() === $expected->id();
}
return parent::matches($event, $expected, $strict);
}
Integration with Laravel
TestUtil in Laravel’s TestCase:
use EventSauce\TestUtilities\TestUtil;
use Orchestra\Testbench\TestCase as LaravelTestCase;
class AggregateTest extends LaravelTestCase
{
use TestUtil;
// ...
}
EventSourcingRepository in TestCase::setUp() for Laravel-specific storage.Performance Testing
$this->assertEvents($aggregate, array_fill(0, 1000, new BulkEvent()));
How can I help you explore Laravel packages today?