我在Symfony 6项目的一个自定义类中使用Symfony mailer,通过类的构造函数中的类型提示使用自动装配,如下所示:
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
是否被正确处理,为此我需要邮件程序在我的测试中抛出TransportException
,然而,这并不像我希望的那样工作。
注意:我不能通过传递无效的电子邮件地址来引发异常,因为sendMail
方法只允许有效的电子邮件地址。
我尝试过的事情:
1)使用模拟邮件
// boot kernel and get Class from container
$container = self::getContainer();
$myClass = $container->get('App\Model\MyClass');
// create mock mailer service
$mailer = $this->createMock(Mailer::class);
$mailer->method('send')
->willThrowException(new TransportException());
$container->set('Symfony\Component\Mailer\Mailer', $mailer);
事实证明,我不能模拟Mailer
类,因为它是final
。
2)使用模拟(或存根)MailerInterface
// create mock mailer service
$mailer = $this->createStub(MailerInterface::class);
$mailer->method('send')
->willThrowException(new TransportException());
$container->set('Symfony\Component\Mailer\Mailer', $mailer);
没有错误,但没有抛出异常。似乎邮件服务没有被替换。
3)使用自定义MailerExceptionTester类
// MailerExceptionTester.php
<?php
namespace App\Tests;
use Symfony\Component\Mailer\Envelope;
use Symfony\Component\Mailer\Exception\TransportException;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\RawMessage;
/**
* 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('Symfony\Component\Mailer\Mailer', $mailer);
结果与2)相同
4)尝试替换MailerInterface服务而不是Mailer
// create mock mailer service
$mailer = $this->createMock(MailerInterface::class);
$mailer->method('send')
->willThrowException(new TransportException());
$container->set('Symfony\Component\Mailer\MailerInterface', $mailer);
错误信息:Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "Symfony\Component\Mailer\MailerInterface" service is private, you cannot replace it.
5)将MailerInterface设置为公共
// services.yaml
services:
Symfony\Component\Mailer\MailerInterface:
public: true
Error: Cannot instantiate interface Symfony\Component\Mailer\MailerInterface
6)为MailerInterface添加别名
// services.yaml
services:
app.mailer:
alias: Symfony\Component\Mailer\MailerInterface
public: true
错误信息:Symfony\Component\DependencyInjection\Exception\InvalidArgumentException: The "Symfony\Component\Mailer\MailerInterface" service is private, you cannot replace it.
如何在测试中替换autowired MailerInterface
服务?
3条答案
按热度按时间46scxncf1#
我正试图做到这一点,我相信我已经找到了一个解决方案的基础上关闭你已经尝试过。
在我的
services.yaml
中,我重新声明了mailer.mailer
服务,并在测试环境中将其设置为public:这个设置应该使Symfony Mailer服务的行为与以前完全相同,但是因为它现在是公共的,所以如果需要,我们可以在容器中覆盖它使用的类。
我复制了你写的自定义Mailer类...
......在我的测试代码中,我获得了测试容器,并用异常抛出类的示例替换了
mailer.mailer
服务:现在,无论在何处注入Mailer服务,使用的类都将是自定义异常抛出类!
izkcnapc2#
第一次尝试的顺序应该是正确的。
未测试,但您将类作为对象获取,因此在模拟之前已将de dependency解析为服务。这应首先替换容器中的服务,然后从容器中获取
MyClass
。然而,您也可以完全跳过容器的构建,只使用PhpUnit。
bvuwiixz3#
对于像我一样发现这个问题并且只想在测试中Assert电子邮件消息的人来说,与其模仿
MailerInterface
,不如使用内置的MailerAssertionsTrait
,它没有模拟传输异常的选项,但是这个特性非常适合邮件内容测试。参考文献: