我正在 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