Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Messenger Test Laravel Package

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...

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. zenstruck/messenger-test is a test-environment-only shim for symfony/messenger. It registers a TestTransport via a test:// DSN, enabled through ZenstruckMessengerTestBundle in config/bundles.php under ['test' => true], and exposes an InteractsWithMessenger trait for KernelTestCase/WebTestCase. Architecturally this is the lowest-risk category of dependency we can take: it never ships to production, has no runtime service footprint outside the test env, and is confined to the transport layer of an existing framework abstraction. For any team already running Symfony Messenger for async work (notifications, webhooks, payment callbacks, projections), this converts "fire-and-forget" into assertable state: queue(), dispatched(), acknowledged(), rejected() collections with assertContains, assertCount, assertHasStamp, plus process(), processOrFail(), unblock()/intercept() and throwExceptions()/catchExceptions(). Built-in serialize/unserialize round-tripping on every intercepted message is the standout feature — it catches the class of bug (non-serializable payloads, closures in stamps) that normally only surfaces in staging.

Integration feasibility. High. Three concrete steps: composer require --dev, register the bundle for test only, and override each transport DSN under when@test in messenger.yaml (including multi-transport setups via $this->transport('high')). Symfony Flex typically handles the bundle registration. No application code changes, no schema changes, no infra changes. Optional capabilities (intercept=false, catch_exceptions=false, test_serialization=false, support_delay_stamp=true) are DSN flags, so behavior is configurable per transport rather than requiring forks or overrides. The only real prerequisite is a PSR-20 clock implementation if we want DelayStamp semantics tested — symfony/clock auto-wires if present, otherwise a manual service alias.

Technical risk. Moderate-low overall, but with three named risks:

  • Contract divergence. Tests then exercise a fake transport, not AMQP/Redis/Doctrine. Broker-specific behavior — retry strategy, dead-letter queues, visibility timeouts, real concurrency, ordering under load — is out of scope. Risk of false confidence if teams treat green tests as broker coverage.
  • Framework coupling. The package tracks Symfony Messenger internals; Messenger majors have historically moved stamps and transport contracts. A Symfony upgrade could strand us on an unsupported pairing. 1.x branch, MIT, 279 stars, and a documented changelog/release cadence are reassuring but this is a small-maintainer project — bus factor of roughly one.
  • Signal quality defaults. Exception catching is on by default (failures land in rejected() rather than throwing), and unblock() is available. Both are convenient and both can hide failures if teams don't assert on rejected() or opt into throwExceptions() in critical suites.

The reported zero dependents is almost certainly a dataset artifact rather than reality; verify on Packagist and by scanning our own Composer graphs before treating adoption risk as unknown.

Key questions for the maintainers/vendor.

  1. Which Symfony and PHP version ranges does 1.x support, and is there a published CI matrix we can align with our own support window?
  2. Can we get a written policy on Messenger major-version support timelines, or is the effective answer "pin and monitor"?
  3. Does the team already have a PSR-20 clock in the container, and do any handlers depend on DelayStamp?
  4. Which of our async flows have broker-specific behavior that this package will not cover, and what's our separate integration-test story for those?
  5. What is our exit cost — is there a viable fallback (in-memory:// plus hand-rolled assertions, or a thin internal fork) if the package goes stale?

Integration Approach

Stack fit. Native fit for a Symfony application on Messenger — it plugs into the existing transport abstraction rather than requiring a new testing framework, new fixtures, or a parallel harness. It composes cleanly with PHPUnit and Symfony's KernelTestCase/WebTestCase, and the fluid ->back() API makes multi-transport assertions reasonably readable. For teams on non-Symfony PHP frameworks the fit is effectively zero; this is a Symfony-only bet.

Migration path. Additive and fully reversible. Adoption is a dev dependency plus a when@test config block; rollback is deleting the dependency and the overrides, with no production artifact to unwind. There is no data migration, no cutover window, and no coordination with release management. This is the rare dependency where the reversibility cost is near zero, which argues for a fast, broad pilot rather than a heavyweight review.

Compatibility. Requires Symfony Messenger with the test:// transport and EnvelopeCollection support in the 1.x line; DelayStamp support dates from 1.8.0 and carries a hard PSR-20 clock requirement. Multi-transport and multi-bus configurations are supported; message buses themselves are asserted separately (dispatched() on the bus). Pre-encoded ['body' => '...'] sends are supported, which matters for replay/backfill test scenarios.

Sequencing.

  1. Week 1 — spike. One squad with an existing Messenger workflow installs the package, overrides a single non-critical transport, and writes 5–10 assertions around dispatch, processing, and rejection. Measure time-to-first-assertion and surface any serialization failures immediately.
  2. Week 2 — conventions. Document the house rules: always assert on rejected(); use throwExceptions() for critical handlers; bound process($n) rather than unbounded loops in suites that fan out; call $this->transport()->reset() (or TestTransport::resetAll() in tear-down) to avoid cross-test leakage.
  3. Weeks 3–5 — broaden by risk. Roll out to the highest-value async surfaces first: payment callbacks, outbound notifications, third-party webhooks. These are where silent dispatch failures turn into customer-visible incidents.
  4. Week 6 — CI gate. Add coverage thresholds on async paths and wire a check that new transports default to test:// in the test env.
  5. Ongoing — broker coverage gap. In parallel, define which flows keep a separate integration suite against a real broker in CI, so the fake transport doesn't become the only safety net.

Operational Impact

Maintenance. Low, but not free. Cost is a dev-dependency bump cadence (track the 1.x releases), a periodic compatibility check against our Symfony upgrade cycle, and ownership of the conventions doc. Realistically a few hours per quarter plus a slot in the Symfony-major-upgrade runbook. Budget for the possibility of patching or forking if the upstream maintainer goes inactive — the code surface is small enough that a fork is a weekend, not a project.

Support. Community-supported MIT project with a single primary maintainer; no SLA and no commercial backing. Acceptable for a test-only dependency where the fallback is "delete it and use in-memory://". It would not be acceptable if it were load-bearing for production behavior, which it is not. Escalation path is internal: whoever onboards the package owns the fork decision.

Scaling. Scales well across teams because it's config-driven and per-transport, so squads adopt independently. Two scaling watch-outs: (a) suite runtime — unbounded process() walks message chains and can be slow or, with recursive dispatch, effectively hang; mandate bounded processing in shared suites; (b) convention drift — without a lint or review checklist, teams will diverge on reset discipline and rejection assertions, and flaky inter-test leakage will erode trust in the suite.

Failure modes to plan for.

  • Silent pass. Test asserts on queue() contents but never processes; handler bugs go undetected. Mitigation: require at least one process() + acknowledged() assertion per async flow.
  • Swallowed exceptions. Default catch_exceptions=true funnels failures to rejected(); a test that only checks the queue is empty will pass while the handler failed. Mitigation: assert rejected()->assertEmpty() as the default pattern, and use test://?catch_exceptions=false for critical transports.
  • Serialization divergence. Round-tripping catches payload bugs but not broker-specific encoding. Mitigation: keep one real-broker integration test per high-value flow.
  • Clock-dependent flakiness. DelayStamp tests depend on a mockable PSR-20 clock; without ClockSensitiveTrait/self::mockTime() discipline, delayed-message tests will be non-deterministic.
  • Cross-test state leakage. Queues persist across tests unless reset; expect a first-wave of flaky suites before tear-down conventions land.
  • Abandonment. Upstream goes quiet mid-Symfony-major; we pin, fork, or revert to in-memory://.

Ramp-up. Low. A developer fluent in Symfony and PHPUnit reaches productive use in roughly half a day, aided by an unusually complete README (queue assertions, processing, exception modes, unblock mode, serialization testing, multi-transport, delay stamps). The API is assertion-style and self-documenting. The real ramp-up cost is not the library but the team habit change: moving from "we dispatched it" to "we dispatched it, it processed, and it did not reject."

Recommendation. Adopt with guardrails. The package is test-scoped, MIT, reversible at near-zero cost, and directly addresses a real reliability gap in async Symfony workflows. Sequence it as a pilot-then-broaden rollout, codify the rejection-assertion and reset conventions up front, and deliberately retain real-broker integration coverage for high-value flows so that green unit suites are never mistaken for broker correctness. The decision to revisit is a defined trigger, not a date: upstream going 6+ months silent, or a Symfony Messenger major that 1.x does not support within our upgrade window.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console