suchen

Heim  >  Fragen und Antworten  >  Hauptteil

Der umgeschriebene Titel lautet: Wie kann ich den Symfony-Mailer zu Testzwecken simulieren?

Ich verwende den Symfony-Mailer in einer benutzerdefinierten Klasse in einem Symfony 6-Projekt. Ich verwende Autowiring über Typhinweise im Konstruktor der Klasse wie folgt:

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,
                ];
            }
        }
    }

Rufen Sie die sendEmail()-Methode im Controller auf und alles funktioniert einwandfrei.

Jetzt möchte ich TransportExceptions 是否被正确处理。为此,我需要邮件程序在我的测试中抛出 TransportExceptions testen. Dies funktionierte jedoch nicht wie erhofft.

HINWEIS : Ich kann keine Ausnahme auslösen, indem ich eine ungültige E-Mail-Adresse übergebe, da die sendMail-Methode nur gültige E-Mail-Adressen zulässt.

Dinge, die ich versucht habe:

1) Verwenden Sie ein simuliertes E-Mail-Programm

// 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);

Es stellte sich heraus, dass ich nicht simulieren konnte Mailer 类,因为它是 final.

2) Verwenden Sie Mock (oder Stub) MailerInterface

// create mock mailer service
$mailer = $this->createStub(MailerInterface::class);
$mailer->method('send')
        ->willThrowException(new TransportException());
$container->set('SymfonyComponentMailerMailer', $mailer);

Keine Fehler, aber keine Ausnahmen ausgelöst. Der Postdienst scheint nicht ersetzt worden zu sein.

3) Verwenden Sie die benutzerdefinierte MailerExceptionTester-Klasse

// 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();
    }
}

Im Test:

// create mock mailer service
$mailer = new MailerExceptionTester();
$container->set('SymfonyComponentMailerMailer', $mailer);

Gleiches Ergebnis wie in 2)

4) Versuchen Sie, den MailerInterface-Dienst anstelle von Mailer

zu ändern
// create mock mailer service
$mailer = $this->createMock(MailerInterface::class);
$mailer->method('send')
        ->willThrowException(new TransportException());
$container->set('SymfonyComponentMailerMailerInterface', $mailer);

Fehlermeldung: SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。

5) MailerInterface auf öffentlich setzen

// services.yaml
services:
    SymfonyComponentMailerMailerInterface:
        public: true

错误:无法实例化接口 SymfonyComponentMailerMailerInterface

6) Alias ​​für MailerInterface hinzufügen

// services.yaml
services:
    app.mailer:
        alias: SymfonyComponentMailerMailerInterface
        public: true

Fehlermeldung: SymfonyComponentDependencyInjectionExceptionInvalidArgumentException:“SymfonyComponentMailerMailerInterface”服务是私有的,您无法替换它。

Wie ersetzt man einen automatisch verbundenen MailerInterface Dienst in einem Test?

P粉739706089P粉739706089349 Tage vor524

Antworte allen(2)Ich werde antworten

  • P粉704066087

    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();
    

    Antwort
    0
  • P粉226667290

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

    Antwort
    0
  • StornierenAntwort