重写后的标题为:How can I simulate the Symfony mailer for testing purposes?
P粉739706089
P粉739706089 2023-12-13 09:42:22
0
2
527

我正在 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() 方法,一切正常。

现在我想测试 TransportExceptions 是否被正确处理。为此,我需要邮件程序在我的测试中抛出 TransportExceptions 。然而,这并没有像我希望的那样起作用。

注意:我无法通过传递无效的电子邮件地址来引发异常,因为 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粉739706089
P粉739706089

全部回复(2)
P粉704066087

您第一次尝试的顺序应该是正确的。

// 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粉226667290

我正在尝试这样做,并且我相信我已经根据您已经尝试过的方法找到了解决方案。

在我的 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 服务,所使用的类都将是自定义异常抛出类!

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板