How do I patch django.core.mail.send_mail for testing?

1,776 views
Skip to first unread message

Andrew Farrell

unread,
May 20, 2015, 12:08:48 AM5/20/15
to django...@googlegroups.com
Hi folks,

I'm trying to use mock.patch to test that a particular post-save signal kicks off an email. My current approach to this is...


from django.test import TestCase

from mock import patch

class RegisterTestCase(TestCase):
    def test_mail_is_sent(self):
        with patch('django.core.mail.send_mail') as mocked_send_mail:
            from register.models import Subscriber  #This test still fails if I have the import outside the mock context
            subscriber = Subscriber.objects.create(
                email = 'te...@example.com',
            )
            self.assertTrue(mocked_send_mail.called)
            self.assertFalse(subscriber.active)
            self.assertEqual(mocked_send_mail.call_args['recipient_list'], 'te...@example.com')

    def test_patching_mail_works(self):
        with patch('django.core.mail.send_mail') as mocked_send_mail:
            from django.core.mail import send_mail   #This test actually fails too if I have the import outside the mock context
            send_mail(subject="foo", message="bar", from_email="Andrew M. Farrell <amfa...@mit.edu>",
                recipient_list = ['Andrew M. Farrell <amfa...@mit.edu>',])
            self.assertTrue(mocked_send_mail.called)

The first test fails while the second test passes.
As I understand from reading the mock.patch documentation, patch('django.core.mail.send_mail') should cause all references to that object to instead referr to an instance of MagicMock.
However, I can verify by inserting a pdb.set_trace() call into the signal handler that send_mail is in fact called during the test and that send_mail.func_code is <code object send_mail at 0x103492330, file "//anaconda/envs/taxbrain/lib/python2.7/site-packages/django/core/mail/__init__.py", line 41>

Are there other things that could cause a function to not be mocked properly?
Is my interpretation of the documentation just incorrect?

thank you,
Andrew

Stephen J. Butler

unread,
May 20, 2015, 2:40:08 AM5/20/15
to django...@googlegroups.com
I think you instead want to use self.modify_settings() or
self.override_settings() to change EMAIL_BACKEND to locmem:

https://docs.djangoproject.com/en/1.8/topics/email/#in-memory-backend

Then you can inspect django.core.mail.outbox to verify recipients, message, etc.
> --
> You received this message because you are subscribed to the Google Groups
> "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to django-users...@googlegroups.com.
> To post to this group, send email to django...@googlegroups.com.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/CA%2By5TLYig%3DiywETuc2pvfe3%3Dajyzc4yhv0QyVWiCj6U16mMH1Q%40mail.gmail.com.
> For more options, visit https://groups.google.com/d/optout.

sclo...@nexapp.ca

unread,
Aug 23, 2018, 9:19:24 PM8/23/18
to Django users
I solved the problem by wrapping the imports of the test-file inside a context manager, as follow:

with patch('django.core.mail.send_mail') as mocked_send_mail:

   
from ModuleToTest.File import functionToTest

That forced the patch to occur before the tested function was actualy imported.
Reply all
Reply to author
Forward
0 new messages