tienvx/pact-messenger-bundle
Symfony bundle that brings Pact consumer-driven contract testing to Symfony Messenger. Verify message-based interactions between services by recording and replaying message expectations in your test suite, helping catch breaking changes early.
This is a Symfony Messenger bundle, not a Laravel package — tienvx/pact-messenger-bundle wires pact-php consumer contract testing into the Messenger middleware stack. As a Laravel dev you'll mostly use it when integrating with Symfony services over a shared broker (RabbitMQ/SQS/AMQP), so treat it as a reference implementation and a test harness you run alongside, not something you composer require into an app/.
Minimal steps:
composer require --dev tienvx/pact-messenger-bundle (needs PHP 8.x, Symfony 5.4/6.x, symfony/messenger, and phpspec/php-pact).config/bundles.php and create config/packages/pact_messenger.yaml.MessageBusInterface, and assert the recorded interaction matches your contract JSON.Look first at:
EnvelopeCollector — the service that snapshots dispatched Envelopes. This is the heart of the package; everything else is plumbing around it.SendMessageToTransportsEventListener — hooks SendMessageToTransportsEvent so messages leaving the bus are captured at exactly the right point.First real use case: you publish App\Message\UserCreated to a queue and a Symfony consumer owns the contract. Use this bundle (or copy its pattern into a Laravel test) to assert your outgoing envelope's body and headers satisfy the consumer's expectations before you ship.
Consumer test with the mock provider. Boot the Pact mock server in setUpBeforeClass, dispatch via MessageBusInterface, then let EnvelopeCollector hand you the captured Envelope for assertion against the mock's expectations. Verify in tearDownAfterClass.
Transport-scoped recording (new in v1.0.0, PR #2). Allow filter envelope by transport is the most practically important change in this release. Configure an allowlist of transport names so only messages routed to the contracted queue are recorded. Without this, every message on every bus — sync, async, failed, retry — lands in the interaction set and your contract drifts.
Assert on the envelope, not the payload. The value of this bundle over hand-rolled HTTP assertions is that it captures the full Envelope: serialized body, Content-Type, type header, and Messenger stamps. Assert the headers your consumer actually keys off.
Provider-side verification. Run the handler against the published pact to prove your Symfony consumer can deserialize what's produced. Pair with pact-broker publish + can-i-deploy in CI.
Mirror the package's own test layout. v1.0.0 added tests for EnvelopeCollector, SendMessageToTransportsEventListener, and a UserController — copy that structure for your own integration tests; the listener unit test in particular shows the exact event shape you need to fake.
Laravel-side adapter. If you're the producer, don't fight the Symfony dependency tree. Either (a) use phpspec/php-pact directly with a small MessageSending/JobProcessing event listener, or (b) run this bundle in a tiny Symfony sidecar whose only job is producing contract interactions. The EnvelopeCollector logic is ~100 lines and easy to port to a Laravel listener.
config/pact.php. Bundle registration and YAML config only. In Laravel you must wire the collector yourself or skip the bundle entirely.RedeliveryStamp, DelayStamp, and error stamps in the headers — noise that will break a strict contract.Serializer must match the consumer's expectations (application/json vs PHP-serialize, type header alias mapping). Serializer mismatches are the #1 cause of "the contract passes locally but the consumer can't deserialize it."in-memory:// for tests. Hitting a real broker makes the suite slow and non-deterministic; the collector only cares about the envelope leaving the bus, not delivery.type header — a breaking contract change that no PHPStan rule will catch.EnvelopeCollector and the config tree directly when the README is ambiguous.EnvelopeCollector (it's a normal service) to redact or normalize headers, and override/replace SendMessageToTransportsEventListener if you need custom capture timing. Both are tested as of this release, so overriding is reasonably safe.How can I help you explore Laravel packages today?