I'm using the Symfony mailer in a custom class in a Symfony 6 project. I'm using autowiring via type hints in the class's constructor like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Call the sendEmail()
method in the controller and everything is fine.
Now I want to test whether TransportException
s are handled correctly. To do this, I need the mailer to throw TransportException
s in my tests. However, this didn't work as I hoped.
Note: I cannot throw an exception by passing an invalid email address because the sendMail
method only allows valid email addresses.
Things I've tried:
1) Use simulated email program
1 2 3 4 5 6 7 8 9 |
|
As a result I cannot mock the Mailer
class because it is final
.
2) Use a mock (or stub) MailerInterface
1 2 3 4 5 |
|
No errors, but no exceptions are thrown. The mail service does not appear to have been replaced.
3) Use a custom MailerExceptionTester class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
In testing:
1 2 3 |
|
Same result as in 2)
4) Try changing the MailerInterface service instead of Mailer
1 2 3 4 5 |
|
Error message: SymfonyComponentDependencyInjectionExceptionInvalidArgumentException: The 'SymfonyComponentMailerMailerInterface' service is private and you cannot replace it.
5) Set MailerInterface to public
1 2 3 4 |
|
Error: Unable to instantiate interface SymfonyComponentMailerMailerInterface
6) Add an alias for MailerInterface
1 2 3 4 5 |
|
Error message: SymfonyComponentDependencyInjectionExceptionInvalidArgumentException: The 'SymfonyComponentMailerMailerInterface' service is private and you cannot replace it.
How to replace the auto-connected MailerInterface
service in a test?
P粉7040660872023-12-14 00:55:20
The order should be correct on your first try.
1 2 3 4 5 6 7 8 9 10 |
|
Not tested, but you are getting the class as an object, so dependencies on the service are already resolved before mocking. This should first replace the service in the container and then get MyClass
from the container.
However, you can also skip building the container entirely. Just use PhpUnit.
1 2 3 4 5 |
|
P粉2266672902023-12-14 00:54:16
I'm trying to do this, and I believe I've found a solution based on what you've already tried.
In my services.yaml
I redeclare the mailer.mailer
service and set it to public in the test environment:
when@test: services: mailer.mailer: class: Symfony\Component\Mailer\Mailer public: true arguments: - '@mailer.default_transport'
This setup should make the Symfony Mailer service behave exactly the same way as before, but because it is now public we can override the classes it uses in the container if needed.
I copied the custom Mailer class you wrote...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|