- What is zenstruck/messenger-test and what does the TestTransport do?
- It is a test-only library for symfony/messenger that registers a TestTransport via a test:// DSN. That transport intercepts messages sent to it so you can inspect and assert against them instead of dispatching them to a real broker. Every intercepted message is serialized and unserialized as a sanity check, and the transport can also process the queued messages on demand. It is intended for KernelTestCase and WebTestCase suites through the InteractsWithMessenger trait.
- How do I install zenstruck/messenger-test in a Symfony app?
- Install it as a dev dependency with composer require --dev zenstruck/messenger-test. Register ZenstruckMessengerTestBundle in config/bundles.php under the test key (Symfony Flex usually does this automatically). Then override each transport DSN under when@test in config/packages/messenger.yaml with test://. No application code, schema, or infrastructure changes are required.
- Does zenstruck/messenger-test work with Laravel?
- No. This is a Symfony bundle built on symfony/messenger transports, the ZenstruckMessengerTestBundle, and Symfony's KernelTestCase, so it has no Laravel integration. Laravel applications should use the framework's own queue fakes and job assertions instead. If you are evaluating it for a Laravel codebase, it is simply not the right tool.
- How do I assert that a message was queued in my test?
- Use the InteractsWithMessenger trait in your test case and call $this->transport()->queue(). The queue collection supports assertions such as assertEmpty, assertNotEmpty, assertCount, and assertContains with a message class, optionally with an expected count. You can also assert on the acknowledged and rejected collections to verify how processing ended, and assertHasStamp to check for a specific stamp.
- How do I process queued messages inside a test?
- Call process() on the test transport to consume and handle everything currently queued, or processOrFail() if a failure should fail the test. You can also use unblock() and intercept() to control whether messages are intercepted or allowed through. Note that exception catching is enabled by default, so failures land in the rejected collection rather than throwing; use throwExceptions() in critical suites if you want them raised.
- How do I configure zenstruck/messenger-test for multiple transports?
- Override each transport DSN with test:// in your when@test configuration, then select the transport in tests with $this->transport('high') and similar. Behaviour is tunable per transport through DSN flags such as intercept=false, catch_exceptions=false, test_serialization=false, and support_delay_stamp=true. That means you can adjust interception and assertion behaviour without forking the package or writing custom overrides.
- What is the serialize/unserialize check and why does it matter?
- By default every intercepted message is serialized and unserialized before it is queued, which surfaces payloads that cannot survive a real transport round trip. This catches non-serializable payloads and closures hidden inside stamps, a class of bug that otherwise only appears in staging or production. You can disable it per transport with test_serialization=false if you deliberately need to skip the check.
- Why are my handler exceptions not failing the test?
- Exception catching is on by default, so a throwing handler results in the message being placed in the rejected collection instead of bubbling up. That is convenient but can hide failures if you never assert on rejected(). Opt into throwExceptions() for critical suites, or add explicit assertions on the rejected collection so regressions cannot slip through silently.
- Which Symfony and PHP versions are supported, and is the package well maintained?
- The 1.x branch is MIT licensed, has a documented changelog, and publishes a CI matrix you should check against your own support window before adopting. It tracks symfony/messenger internals, and Messenger majors have historically moved stamps and transport contracts, so a Symfony upgrade could strand you on an unsupported pairing. It is a small-maintainer project, so treat it as pin-and-monitor and confirm the exact version constraints on Packagist.
- Can it test delayed messages and does it replace integration tests against a real broker?
- DelayStamp semantics can be exercised when a PSR-20 clock is available in the container (symfony/clock auto-wires if installed) along with the support_delay_stamp flag. However, the test:// transport is a fake, so broker-specific behaviour such as retry strategies, dead-letter queues, visibility timeouts, real concurrency, and ordering under load is out of scope. Keep separate integration tests against AMQP, Redis, or Doctrine so green unit tests are not mistaken for broker coverage.