PageView

Subscribe to our YouTube Channel!
[Jul 12, 2021] New Video: How to Use Django Rest Framework Permissions (DRF Tutorial - Part 7)


Ask Vitor #3: Mocking Emails

Ask Vitor #3: Mocking Emails

Phillip Ahereza asks:

I’m writing unit tests for my django app and I was wondering if there are any packages for mocking email or if there is any way I could mock sending and receiving of emails.


Answer

Basically what Django does when you run your test suite is switch your EMAIL_BACKEND to django.core.mail.backends.locmem.EmailBackend, so to prevent your application from sending emails during the tests execution.

While using this backend, all emails sent are stored in the outbox attribute of the django.core.mail module.

Let’s see one example on how you can use it to test the email outputs and so on.

urls.py

from django.conf.urls import url
from mysite.core import views

urlpatterns = [
    url(r'^send/$', views.send, name='send'),
]

views.py

from django.http import HttpResponse
from django.core.mail import send_mail

def send(request):
    email = request.GET.get('email')
    if email and '@' in email:
        body = 'This is a test message sent to {}.'.format(email)
        send_mail('Hello', body, 'noreply@mysite.com', [email, ])
        return HttpResponse('<h1>Sent.</h1>')
    else:
        return HttpResponse('<h1>No email was sent.</h1>')

This is a simple view that expects a querystring parameter named email with a valid email address. If the email value fulfill our view requirements, an email is sent to this address. If the email is invalid or no email is provided at all, the view just return a message for the user.

Now, let’s write some unit tests for it. First, a test case in case no email is provided:

tests.py

from django.core import mail
from django.core.urlresolvers import reverse
from django.test import TestCase

class EmailTest(TestCase):
    def test_no_email_sent(self):
        self.response = self.client.get(reverse('send'))
        self.assertEqual(len(mail.outbox), 0)

We can also write a test case and inspect the email contents:

tests.py

from django.core import mail
from django.core.urlresolvers import reverse
from django.test import TestCase

class EmailTest(TestCase):
    def test_no_email_sent(self):
        self.response = self.client.get(reverse('send'))
        self.assertEqual(len(mail.outbox), 0)

    def test_email_sent(self):
        self.response = self.client.get(reverse('send'), {'email': 'test@example.com'})
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].body, 'This is a test message sent to test@example.com.')

Final Remarks

Simple as that! You can find more information about the testing tools and email services on the official documentation: Django Testing Tools - Email Services.

You can also find the source code used in this post on Github: github.com/sibtc/askvitor.