Closed (fixed)
Project:
Drupal core
Version:
11.x-dev
Component:
mail system
Priority:
Normal
Category:
Task
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
28 Oct 2023 at 09:50 UTC
Updated:
11 Dec 2025 at 17:49 UTC
Jump to comment: Most recent
Comments
Comment #2
znerol commentedComment #3
znerol commentedPushed code removed in #3379794: Add symfony mailer transports to Dependency Injection Container (mail delivery layer). I think this could be much less complex if we'd just replace the
mailer.transportservice directly with the capturing mail transport. Then themailer_dsnconfig wouldn't have any effect at all and we wouldn't need to override the override it in tests.Comment #4
znerol commentedComment #5
znerol commentedComment #6
znerol commentedComment #8
smustgrave commentedWasn't sure how to test this one.
I installed the new test modules and clicked "Send mail"
Then I checked the db directly and see the mailer_capture entry in key_value table.
Code wise LGTM
Comment #9
adamps commentedThis looks good.
It would be interesting to check if this still works in the case that a Contrib module has overridden the service for
TransportInterface. It seems like it would, which is good news.Which categories of testing does this work for (i.e. Unit, Kernel, Functional)? All 3 categories are likely to be useful.
There is a similar issue #3395814: Trait for testing Symfony Emails, and good news is that the proposed solutions are similar. The other issue goes an extra step, proposing a full collection of assert functions for efficiently checking emails, for example:
assertBodyContains,assertSubject,assertTo. The functions operate on the email at the front of the queue, and the functionreadMail()removes an email from the queue and then testing begins on the next one. This reduces the amount of test code substantially compared with each module having to navigate the email array directly. So the other issue can remain as a follow-up - or if Core doesn't want it, then it can remain in Contrib. See MailerTestTrait in DSM+.Comment #10
adamps commentedComment #11
adamps commentedI tried to integrate this patch into DSM+. I found that
MailerCaptureTrait::getMails()currently returnsSentMessagerather thanEmailas indicated by the comment. I fixed it as below, removing the reference toAbstractTransport.In the tests for this patch we should add some asserts on fields on the email, which would prevent errors like this from being introduced.
EDIT: fixed return value
Comment #12
adamps commentedFollow up from #9.
I like it - it has some clear improvements over the current DSM+. I have made an issue to change DSM+ to match this #3533041: Alter mailer testing to match Core proposal. I'll wait until this is committed.
Also DSM+ has some clear improvements over this. We can use #3395814: Trait for testing Symfony Emails if we wish to add them to Core.
I made some detailed comments in the MR.
Comment #13
adamps commentedBelow is a comparison of the code we could have in
MailerCaptureTestbefore and after the trait added in #3395814: Trait for testing Symfony Emails. The character count reduces from 559 to 264 = 53% reduction, and it is more readable IMO.The trait itself is 300 lines, so not a big maintenance burden. Given that, I hope we might add it to core. If so, then it would potentially make sense to adjust this issue to have module
mailer_testcontainingMailerTestTrait.Comment #14
znerol commentedThanks for the review. Regarding #11, we should stick with
AbstractTransport, otherwise transport events will not fire. I've added methods for both cases (getEmails()for assertions on the originalEmailandgetCapturedMessages()for assertions onSentMessage).Comment #15
adamps commentedThanks that looks good. How do you feel about #13?
Comment #16
adamps commentedBefore this gets committed I feel we should discuss #13 please.
In preparation for #3395814: Trait for testing Symfony Emails it would likely make sense to adjust this issue to have module
mailer_testcontainingMailerTestTrait. Otherwise the follow on issue creates extra work for us and the core committers by renaming everything.Comment #17
znerol commentedSorry, I do not get it. What needs to be renamed and when?
Comment #18
adamps commentedThe module name is currently mailer_capture. This is good for the current use case.
In #3395814: Trait for testing Symfony Emails I propose to add some extra functions that I believe will be very useful, see also #13 explaining the benefits. These functions would naturally live in the same module, probably even the same trait. Therefore I propose that we change the module name now to mailer_test, and the trait to MailerTestTrait. The module/trait are responsible for assisting testing of emails, which includes both capturing the email and asserting the values. Developers will almost always want both parts I believe, so we don't need a module for each.
How do you feel about that??
Comment #19
znerol commentedI think the additional assertions could be useful in unit tests too. For that reason it would be better to keep mail capturing and test assertions separate. The former only works in kernel and browser tests. The latter will work everywhere.
Comment #20
adamps commentedOK that makes sense.
Is it OK with you if I add #3395814: Trait for testing Symfony Emails into the roadmap??
Comment #21
berdirI'm not convinced that providing a test API in a test module is the right call here and replacing the transport completely, so essentially I challenge #3. We already customize the mailer_dsn in test setup and expect this to work. I think it will be confusing for example if you want to have your own collector for something or test your own transport (and maybe mock it's internal API or something) and then that won't work. See comments.
Comment #22
adamps commented+1
There are two main classes in this issue: the capture transport, and the API for accessing captured emails. We can put these in the natural location as @Berdir suggests - not inside a test module or anything.
Then we just need a mechanism for enabling the capture transport. It's much less important where this is located because the exact mechanism is internal. It could be a test module, however I feel there is likely a better option. My impression is that the experimental mail plugin is mostly just a proof of concept / placeholder, and maybe we could even delete it when the new mailer lands. Even if we don't, then could we keep testing of the experimental module separate (a minority case) from normal testing, e.g. with a separate base class hence provide a separate dsn?
Comment #23
znerol commentedMoved
MailerCaptureTraitto core, properly inject thekeyvalueservice intoCaptureTransport.Regarding how the transport is enabled, see the comment in the MR. Also keep in mind that the
CaptureTransportrequires (supports testing of) the experimentalmailermodule. That alone should be reason enough to not enable it by default.Comment #24
adamps commentedThanks for the updates (not yet checked). Personally I prefer the existing name of
AssertMailTraitor similar. It would then be natural to add extra functions with specific asserts in #3395814: Trait for testing Symfony Emails.Comment #25
adamps commentedLooks good to me, thanks @znerol. I will leave in NR until @berdir checks.
You can ignore my previous remark as we can change the name as part of #3395814: Trait for testing Symfony Emails (I suggest MailerTestTrait).
Comment #26
znerol commentedUpdate and rebase for #3534354: Add KeyValueFactoryInterface and KeyValueExpirableFactoryInterface alias to core services.
Comment #27
berdirI see the problem with the config for the old API vs. the new API and the whole thing being an opt-in through the experimental module.
Looking through the existing mailer test, we have existing test coverage about not sending mails in \Drupal\Tests\mailer\Functional\TransportServiceFactoryTest, and also a kernel test and both are essentially based on the system.mail:mailer_dsn config, so setting that *does* work. But if I understood correctly, the problem is that setting that explicitly to a "capture" scheme instead of null would not work for \Drupal\Core\Mail\Plugin\Mail\SymfonyMailer. We could work around that with some hardcoded logic that sets capture back to null and say we don't support that there. Or we actually do add support for that in there ourself. It is just another transport.
That said, I can also live with the module that you have to enable for now, we can re-evaluate that when we make the new mailer API non-experimental and switch existing mail sending and their tests over.
What we do need is a change record that describes how to do that I think, even if this is experimental. Needs work for that.
Comment #28
znerol commentedAdded a section to the existing CR.
Comment #29
berdirSetting this back to RTBC. Leaving it to a committer to decide on whether or not we want to have this opt-in switch with a test module or a different approach, see #27 and previous discussions.
Comment #30
znerol commentedSlightly extended the issue summary.
There was an issue link under Remaining tasks which doesn't belong there (a follow up doesn't need to be resolved before this issue can be committed). The follow-up #3395814: Trait for testing Symfony Emails is linked from the related issue list.
Comment #31
adamps commentedOur code is experimental, so we are allowed to change things after commit. Even so I feel we should do our best to get things right first time to minimise confusion/disruption for those evaluating/testing the experimental module. Is there are reason why we can't do that here? We are implementing the test API which will become widely used in module test code.
1) The mailer capture transport should always be enabled when running tests using the experimental mailer (same as in the old mail system). We don't need to ask developers to enable a test module to make it work.
I hear the discussion about possible technical difficulties, however surely there must be a way as we can change any code that we like. There are some suggestions in #27
2) Change the trait name now so we don't have to rename it in #3395814: Trait for testing Symfony Emails. Our goal is one trait that covers both capturing and asserting - tests will always need both and they don't need to use two separate traits. (Perhaps a unit test only wants asserting, if we ever write one - fine it can just ignore the capturing part.) I propose MailerTestTrait, which matches DSM+ and is similar to the name AssertMailTrait in the old mailsystem. If you prefer a different name that's OK by me if it covers both parts: capture and assert.
Then in the IS "proposed resolution" we can remove the reference to the mailer_capture module and instead add a reference to the trait.
How do you feel about that??
Comment #32
chi commentedThe published change record https://www.drupal.org/node/3519253 refers to
mailer_capturewhich is not available yet. That's very confusing.Comment #33
znerol commentedI added the issue link to the mail capturing section.
Comment #35
dcam commentedThe MR for this issue was identified as having a new Kernel/Functional test class that did not have the
#[RunTestsInSeparateProcesses]attribute. A deprecation warning is now issued in the case of these omissions. I've rebased the MR added the attribute to prevent this from being committed as-is and accidentally breaking tests on HEAD. Because this is a minor change to test metadata and the tests are passing I am leaving the issue's status as RTBC.Comment #36
znerol commentedThank you @dcam.
Comment #37
chi commentedSymfony\Component\Mailer\SentMessageis not quite friendly for asserting HTML emails. In functional tests you will likely fetch the original message. I wonder if it is better to capture the original message instead?Here is the implementation I am using for acceptance tests (DTT).
The outbox service also relies on keyValue storage and provide a few helper methods.
Comment #38
znerol commentedTrue. This MR allows access to either of them.
getEmails()returns the originalEmailobjects, whilegetCapturedMessages()returns a list ofSentMessage.Comment #39
longwaveThanks for continuing to work on this. Eligible only for 11.x as the experimental mailer has been removed from 11.3.x.
Committed and pushed 4b2b8604a46 to 11.x. Thanks!