[FrameworkBundle][Messenger] Add MessengerAssertionsTrait to test in-memory transports - #65900
Open
nicolas-grekas wants to merge 1 commit into
Open
[FrameworkBundle][Messenger] Add MessengerAssertionsTrait to test in-memory transports#65900nicolas-grekas wants to merge 1 commit into
nicolas-grekas wants to merge 1 commit into
Conversation
nicolas-grekas
force-pushed
the
messenger-test-assertions
branch
from
September 8, 2026 09:36
b4e6c52 to
f33c36a
Compare
alexandre-daubois
left a comment
Member
There was a problem hiding this comment.
Yes please! Great idea, I remember having to install third party libs to do that.
wachterjohannes
requested changes
Sep 11, 2026
wachterjohannes
left a comment
Member
There was a problem hiding this comment.
Good mechanism overall, the in-memory transport becoming listable and the idle-stop listener are the right building blocks. Replied on alexandre-daubois's retry comment: the fix isn't a one-line gate, the retry/delay interaction needs a design answer first.
nicolas-grekas
force-pushed
the
messenger-test-assertions
branch
2 times, most recently
from
September 13, 2026 07:15
e59dda9 to
ed6b1d0
Compare
…memory transports A functional test that wants to run the messages queued on an in-memory transport has to build a Worker by hand, with an event dispatcher and a StopWorkerOnMessageLimitListener. FrameworkBundle ships assertion traits for Mailer, Notifier, HttpClient and Console but none for Messenger. Messenger: - StopWorkerOnIdleListener stops the worker as soon as its receivers return no message, so a worker can drain a queue and exit without sleeping. - InMemoryTransport implements ListableReceiverInterface and MessageCountAwareInterface: all(), find() and getMessageCount() expose the queued envelopes, delayed ones included. The messenger:show and messenger:stats commands work with in-memory transports as a result. FrameworkBundle: - MessengerAssertionsTrait, composed into KernelTestCase, provides assertQueuedMessageCount(), getQueuedMessages(), getMessengerTransport() and consumeQueuedMessages(). The last one runs the queued messages through the real message bus with the application's event dispatcher, so the retry and failure transport listeners behave as in production, and rethrows the first handler failure so that it fails the test. - Listing and consuming queued messages need symfony/messenger 8.2. With an older version the trait throws a LogicException that says so, like the other Messenger 8.2 features of the bundle.
nicolas-grekas
force-pushed
the
messenger-test-assertions
branch
from
September 13, 2026 13:58
ed6b1d0 to
c7556a3
Compare
wachterjohannes
approved these changes
Sep 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a proposal, open for discussion. It started from Dariusz Gafka's article Symfony Messenger vs Ecotone: The Real Difference, which describes how Ecotone approaches this. What is proposed here is a free interpretation for Symfony, built on Messenger's own model rather than ported from Ecotone, so it departs from the article where the two models differ. The examples in this description are the article's.
Testing a flow whose second step is asynchronous currently means building a
Workerby hand in the test, with anEventDispatcherand aStopWorkerOnMessageLimitListener, and handler failures never surface because the worker turns them into events. FrameworkBundle ships assertion traits for Mailer, Notifier, HttpClient and Console, but nothing for Messenger. This PR addsMessengerAssertionsTraittoKernelTestCase:The transport must be configured as
in-memory://in the test environment; the helpers fail with an explicit message otherwise.Public API
FrameworkBundle,
Symfony\Bundle\FrameworkBundle\Test\MessengerAssertionsTrait(used byKernelTestCase, so also byWebTestCase):assertQueuedMessageCount(int $count, string $transport, ?string $messageClass = null, string $message = ''): number of envelopes queued on the transport, delayed ones included, optionally only the messages that are instances of the given class. A class name that does not exist is rejected, so a filter cannot silently match nothing after a rename.getQueuedMessages(string $transport): Envelope[]getMessengerTransport(string $transport): InMemoryTransport: the transport service, with a clear failure when the transport is not registered or not in-memory.consumeQueuedMessages(string $transport, ?int $limit = null): int: runs a realWorkeron the transport with the application's routable bus and event dispatcher, stops when the transport is empty or after$limitmessages, and returns the number of messages handled.The application's worker listeners run as in production, with one difference that a test needs: a message sent for retry is consumed again without waiting for its delay, so the retries play out inside the call instead of leaving the message parked for
retry_strategy.delayseconds. What that gives:Messenger:
StopWorkerOnIdleListener: stops the worker as soon as its receivers return no message (drain and exit).InMemoryTransportimplementsListableReceiverInterface(all(),find()) andMessageCountAwareInterface(getMessageCount()), which also makesmessenger:showandmessenger:statsaccept in-memory transports.Checks
./phpunit src/Symfony/Component/Messenger/Testsand./phpunit src/Symfony/Bundle/FrameworkBundle/Tests: green (usual missing-server skips). The new functional test app (Tests/Functional/app/Messenger) covers counting, the class filter, the limit, the rethrow and both failure messages ofgetMessengerTransport().willRetry(), a handler that recovers on its retry still aborts the call; without the class check, the unknown-class test passes silently.The functional test app keeps
retry_strategy.delay: 10000on purpose: the retry tests prove that a ten second delay does not make the test wait.Documentation
in-memory://inwhen@test; the four methods and their semantics (delayed messages are counted and listed; envelopes, not messages, are returned).consumeQueuedMessages()runs the real bus and listeners; that retries are replayed without their delay while application delays are honoured; which failure is rethrown and which is not; and how to inspect the failure transport afterwards.StopWorkerOnIdleListenerfor hand-built workers.