Testing asynchronous events with an in-memory bus spy

Testing that domain events are published should not require spinning up RabbitMQ containers in unit test suites or mocking complex message transport interfaces.

final class InMemoryEventBusSpy implements EventBusInterface
{
    /** @var list<object> */
    private array $dispatched = [];

    public function dispatch(object $event): void
    {
        $this->dispatched[] = $event;
    }

    public function assertDispatched(string $eventClass, callable $predicate): void
    {
        $filtered = array_filter($this->dispatched, fn($e) => $e instanceof $eventClass && $predicate($e));
        PHPUnitFrameworkAssert::assertNotEmpty($filtered, "Event {$eventClass} was not dispatched.");
    }
}

An in-memory spy replaces dozens of mock expectations with clean, readable assertion helpers that execute in sub-millisecond memory without mocking libraries.