Testing the email sending in Magento 2

Magento 2, Testing, Integration test

Recently I had the task to add a few email templates to a Magento 2 extension that would get sent on certain events. But there were also some conditions regarding whether the email should get sent or not. That sounded like a good option to test these scenarios, but I've never written tests for sending emails.

TransportBuilderMock

It turns out that Magento has a built-in mock called TransportBuilderMock. This mock extends Magento's TransportBuilder and overrides the reset and getTransport methods, and adds the getSentMessage method. But how do you use these? It turns out to be quite simple.

How is it implemented?

For running tests, Magento has the TestFramework available. This contains an Application class, and in this class, there is an initialize method. In this method, Magento configures itself so that it can run tests. One of the things it does is setting a preference for the TransportBuilder to the TransportBuilderMock. So anytime the TransportBuilder is requested in the test the TransportBuilderMock is returned. 

Using the mock

When writing your test you only have to request the TransportBuilderMock from the ObjectManager, and from the instance you can retrieve the sent message and perform assertions against it. This looks something like this:

public function testMySuperEmail()
{
    $this->prepareMySuperAwesomeTest();

    $this->specificService->sendThisAwesomeEmail();

    /** @var TransportBuilderMock $transportBuilder */
    $transportBuilder = $this->objectManager->get(TransportBuilderMock::class);
    $subject = $transportBuilder->getSentMessage()->getSubject();

    $this->assertEquals('Some awesome news for you!', $subject);
}

The getSentMessage returns an instance of \Magento\Framework\Mail\Message. This allows you to get the body of the email by using getBody. Using the getRawMessage you get the whole message, including the subject, body, and all addresses.

Checking if it did not send

Next to checking if the email is what you expect, you can also check if there is actually sent an email. You can do this by checking for a null return value on the getSentMessage method like so:

public function testMySuperEmail()
{
    $this->prepareMySuperAwesomeTest();

    $this->specificService->sendThisAwesomeEmail();

    /** @var TransportBuilderMock $transportBuilder */
    $transportBuilder = $this->objectManager->get(TransportBuilderMock::class);

    $this->assertNull($transportBuilder->getSentMessage());
}

Conclusion

Although I really couldn't find any documentation on this subject, it turns out to be quite easy to test the email sending. The only downside is that sending emails is one of the most complex things in Magento, and debugging using xDebug can be quite slow. 

Want to respond?