我正在 Symfony 6 项目的自定义类中使用 Symfony 邮件程序。我通过类的构造函数中的类型提示使用自动装配,如下所示:
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, ]; } } }
在控制器中调用 sendEmail()
方法,一切正常。
现在我想测试 TransportException
s 是否被正确处理。为此,我需要邮件程序在我的测试中抛出 TransportException
s 。然而,这并没有像我希望的那样起作用。
注意:我无法通过传递无效的电子邮件地址来引发异常,因为 sendMail
方法只允许有效的电子邮件地址。
我尝试过的事情:
1) 使用模拟邮件程序
// 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);
结果我无法模拟 Mailer
类,因为它是 final
。
2)使用模拟(或存根)MailerInterface
// create mock mailer service $mailer = $this->createStub(MailerInterface::class); $mailer->method('send') ->willThrowException(new TransportException()); $container->set('SymfonyComponentMailerMailer', $mailer);
没有错误,但不抛出异常。邮件服务似乎没有被取代。
3) 使用自定义 MailerExceptionTester 类
// 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(); } }
在测试中:
// create mock mailer service $mailer = new MailerExceptionTester(); $container->set('SymfonyComponentMailerMailer', $mailer);
与 2) 中的结果相同
4)尝试更换MailerInterface服务而不是Mailer
// create mock mailer service $mailer = $this->createMock(MailerInterface::class); $mailer->method('send') ->willThrowException(new TransportException()); $container->set('SymfonyComponentMailerMailerInterface', $mailer);
错误消息:SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。
5) 将 MailerInterface 设置为公开
// services.yaml services: SymfonyComponentMailerMailerInterface: public: true
错误:无法实例化接口 SymfonyComponentMailerMailerInterface
6) 为 MailerInterface 添加别名
// services.yaml services: app.mailer: alias: SymfonyComponentMailerMailerInterface public: true
错误消息:SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。
如何在测试中替换自动连接的 MailerInterface
服务?
P粉7040660872023-12-14 00:55:20
您第一次尝试的顺序应该是正确的。
// boot kernel and get Class from container $container = self::getContainer(); $container->set('Symfony\Component\Mailer\Mailer', $mailer); // create mock mailer service $mailer = $this->createMock(Mailer::class); $mailer->method('send') ->willThrowException(new TransportException()); $myClass = $container->get('App\Model\MyClass');
未经测试,但您将类作为对象获取,因此在模拟之前已经解决了对服务的依赖关系。这应该首先替换容器中的服务,然后从容器中获取MyClass
。
但是,您也可以完全跳过容器的构建。只需使用 PhpUnit。
$mock = $this->createMock(Mailer::class); // ... $myClass = new MyClass($mock); $myClass->sendEmail();
P粉2266672902023-12-14 00:54:16
我正在尝试这样做,并且我相信我已经根据您已经尝试过的方法找到了解决方案。
在我的 services.yaml
中,我重新声明 mailer.mailer
服务,并在测试环境中将其设置为公共:
when@test: services: mailer.mailer: class: Symfony\Component\Mailer\Mailer public: true arguments: - '@mailer.default_transport'
此设置应该使 Symfony Mailer 服务的行为方式与以前完全相同,但是因为它现在是公共的,所以如果需要,我们可以覆盖它在容器中使用的类。
我复制了您编写的自定义 Mailer 类...
// MailerExceptionTester.php...在我的测试代码中,我获取了测试容器并将
mailer.mailer
服务替换为异常抛出类的实例:$mailer = new MailerExceptionTester(); static::getContainer()->set('mailer.mailer', $mailer);现在,无论注入 Mailer 服务,所使用的类都将是自定义异常抛出类!
回复0