Testing the email sending in Magento 2

Magento 2, Testing, Integration test

Recently I had the task of adding 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. 

MageTested.com (ad)

Do you want your Magento store to be more reliable? Tired of things that suddenly break the checkout process without anyone noticing? You can hire my services to kickstart End-2-End testing for your Magento store. This way you know for sure that your store is behaving as expected before releasing that new feature or that update.

View MageTested.com for more information.

Using the mock

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

use Magento\TestFramework\Mail\Template\TransportBuilderMock;

// ...

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 an email sent. You can do this by checking for a null return value on the getSentMessage method like so:

use Magento\TestFramework\Mail\Template\TransportBuilderMock;

// ...

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 was 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?