zenstruck/messenger-test
TestTransport for symfony/messenger that intercepts queued messages so you can inspect and assert against them in your tests. Sent messages are serialized and unserialized as a check, and the transport can also process queued messages. Use the InteractsWithMessenger trait to assert queue state, mess...
test environment.assertCount, assertContains, assertEmpty, acknowledged(), and rejected(), PMs can add explicit "queue behaviour is tested" criteria to acceptance criteria without asking engineering to invent a bespoke harness.DelayStamp with a mocked clock and to assert "nothing happens yet, then it does" turns speculative roadmap commitments into testable ones.rejected() vs acknowledged() visibility, plus throwExceptions() / catchExceptions(), lets the team encode product decisions — e.g. "a failed invoice message must land in rejected, not silently vanish" — as durable, executable expectations.intercept() vs unblock() is a product-relevant lever: intercept mode for testing dispatch contracts, unblock mode for testing end-to-end handler behaviour. PMs can specify which class of behaviour a story needs verified rather than accepting "we tested it manually once."Adopt when all or most of these hold:
symfony/messenger with at least one real transport (AMQP, Redis, Doctrine, SQS) rather than only synchronous handling.KernelTestCase / WebTestCase integration tests and wants queue coverage inside that existing style, not a new test framework.symfony/clock).messenger.yaml overrides — i.e., you're comfortable with test:// DSNs scoped to the test environment only.Look elsewhere when:
test:// config overrides per transport; misconfiguration here silently weakens test coverage rather than failing loudly.Practical caveat for the pitch: the package is dev-only (--dev), so it has no production runtime footprint — adoption risk is bounded to test suites.
For executives (one paragraph): Our async features are the ones most likely to fail silently in production — a confirmation email, a payment retry, or a sync job that never runs looks fine in the UI and surfaces days later as a support ticket or a refund. This is a small, free, dev-only add-on for our Symfony stack that makes those behaviours explicitly tested before release: we can assert that a message was queued, that it was processed, and that it was rejected rather than lost. Cost is roughly a config change plus a test-writing habit; the return is catching a whole class of launch-blocking bugs in CI instead of in production. It carries no production runtime code and is easy to remove if it doesn't earn its place.
For engineering leads: It's composer require --dev zenstruck/messenger-test, register the bundle under test, and point your transports at test:// in the when@test block. You get a TestTransport plus an InteractsWithMessenger trait compatible with existing KernelTestCase/WebTestCase — no new test framework. Defaults are sensible: messages are intercepted, messages are serialized/unserialized as a correctness check, and failures land in rejected() rather than blowing up the test. You can flip to unblock() for end-to-end handler testing, throwExceptions() when you want to assert on the exception path, and support_delay_stamp=true with symfony/clock for deterministic time-based tests. Multiple transports are supported by name, and per-transport DSN flags let you tune behaviour without code.
Explicit non-goals to set expectations: It does not replace broker-level testing (real SQS/AMQP redelivery, dead-letter queues, concurrency) and does not cover non-Symfony queues. It should sit alongside, not instead of, a staging soak test for critical async paths — and it's worth scheduling a small proof-of-concept on one high-value queue before standardising it across the codebase.
How can I help you explore Laravel packages today?