J'utilise le mailer Symfony dans une classe personnalisée dans un projet Symfony 6. J'utilise le câblage automatique via des indices de type dans le constructeur de la classe comme ceci :
class MyClass { public function __construct(private readonly MailerInterface $mailer) {} public function sendEmail(): array { // Email is sent down here try { $this->mailer->send($email); return [ 'success' => true, 'message' => 'Email sent', ]; } catch (TransportExceptionInterface $e) { return [ 'success' => false, 'message' => 'Error sending email: ' . $e, ]; } } }
Appelez la méthode sendEmail()
dans le contrôleur et tout fonctionne bien.
Maintenant, je veux tester les TransportException
s 是否被正确处理。为此,我需要邮件程序在我的测试中抛出 TransportException
. Cependant, cela n’a pas fonctionné comme je l’espérais.
REMARQUE : Je ne peux pas lever d'exception en transmettant une adresse e-mail invalide car la méthode sendMail
n'autorise que les adresses e-mail valides.
Choses que j'ai essayées :
1) Utilisez un programme de messagerie simulé
// boot kernel and get Class from container $container = self::getContainer(); $myClass = $container->get('AppModelMyClass'); // create mock mailer service $mailer = $this->createMock(Mailer::class); $mailer->method('send') ->willThrowException(new TransportException()); $container->set('SymfonyComponentMailerMailer', $mailer);
Il s'est avéré que je ne pouvais pas simuler Mailer
类,因为它是 final
.
2) Utilisez une maquette (ou un stub) MailerInterface
// create mock mailer service $mailer = $this->createStub(MailerInterface::class); $mailer->method('send') ->willThrowException(new TransportException()); $container->set('SymfonyComponentMailerMailer', $mailer);
Aucune erreur, mais aucune exception levée. Le service de messagerie ne semble pas avoir été remplacé.
3) Utilisez la classe MailerExceptionTester personnalisée
// MailerExceptionTester.php <?php namespace AppTests; use SymfonyComponentMailerEnvelope; use SymfonyComponentMailerExceptionTransportException; use SymfonyComponentMailerMailerInterface; use SymfonyComponentMimeRawMessage; /** * Always throws a TransportException */ final class MailerExceptionTester implements MailerInterface { public function send(RawMessage $message, Envelope $envelope = null): void { throw new TransportException(); } }
En test :
// create mock mailer service $mailer = new MailerExceptionTester(); $container->set('SymfonyComponentMailerMailer', $mailer);
Même résultat qu'en 2)
4) Essayez de changer le service MailerInterface au lieu de Mailer
// create mock mailer service $mailer = $this->createMock(MailerInterface::class); $mailer->method('send') ->willThrowException(new TransportException()); $container->set('SymfonyComponentMailerMailerInterface', $mailer);
Message d'erreur : SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。
5) Définissez MailerInterface sur public
// services.yaml services: SymfonyComponentMailerMailerInterface: public: true
错误:无法实例化接口 SymfonyComponentMailerMailerInterface
6) Ajouter un alias pour MailerInterface
// services.yaml services: app.mailer: alias: SymfonyComponentMailerMailerInterface public: true
Message d'erreur : SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。
Comment remplacer un service MailerInterface
automatiquement connecté dans un test ?
La commande devrait être correcte dès votre premier essai.
Non testé, mais vous obtenez la classe en tant qu'objet, donc les dépendances sur le service sont déjà résolues avant de se moquer. Cela devrait d'abord remplacer le service dans le conteneur, puis récupérer
MyClass
du conteneur.Cependant, vous pouvez également ignorer complètement la construction du conteneur. Utilisez simplement PhpUnit.
J'essaie de faire cela et je crois avoir trouvé une solution basée sur ce que vous avez déjà essayé.
Dans mon
services.yaml
中,我重新声明mailer.mailer
service et rendez-le public dans l'environnement de test :Cette configuration devrait permettre au service Symfony Mailer de se comporter exactement de la même manière qu'avant, mais comme il est désormais public, nous pouvons remplacer les classes qu'il utilise dans le conteneur si nécessaire.
J'ai copié la classe Mailer personnalisée que vous avez écrite...