5

I need to test that mail is sent from a Django 1.8 application; the documentation is clear on how to do this, e.g.

https://docs.djangoproject.com/en/stable/topics/testing/tools/#email-services

Here's some code which should therefore suffice:

from myapp.utils.mailutils import mail as mymail
from django.core import mail

def testThisFails(self):
    user = User.objects.filter(id=1).__getitem__(0)
    mymail(user,'Test Message','Test message content, please ignore...')
    self.assertEquals(len(mail.outbox), 1)
    self.assertEquals(mail.outbox[0].subject, 'Test Message')

...obviously, I have proper tests as well. Anyway, I get nothing but this:

self.assertEquals(len(mail.outbox), 1)
AssertionError: 0 != 1

Here's a similar question mentioning that the locmail backend needs to be used:

Django 1.3: Outbox empty during tests

So, I added this to settings.py:

TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
if TESTING:
    EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

...with no luck. Even omitting the if TESTING doesn't address the issue. Is there any means by which I can get my tests to use this backend directly?

djvg
  • 11,722
  • 5
  • 72
  • 103
knirirr
  • 1,860
  • 4
  • 23
  • 37
  • 2
    For me the problem was, that the user had the `email` field empty. Yet `send_mail` didn't show any errors. – prajmus Jun 06 '19 at 16:04
  • @prajmus, thanks for the hint: In our case no emails showed up using [mail_admins()](https://docs.djangoproject.com/en/stable/topics/email/#mail-admins). Turns out our local dev settings did not define any email addresses in [ADMINS](https://docs.djangoproject.com/en/stable/ref/settings/#admins). – djvg Sep 06 '21 at 19:40

2 Answers2

6

Use self.settings context manager for overriding settings

def testThisFails(self):
    # do first here
    user = User.objects.filter(id=1).__getitem__(0)
    with self.settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend'):
        mymail(user,'Test Message','Test message content, please ignore...')
        self.assertEquals(len(mail.outbox), 1)
        self.assertEquals(mail.outbox[0].subject, 'Test Message')

Also use first or get instead of __getitem__. Which is magic method for doing [0] call

user = User.objects.filter(id=1).first()
Sardorbek Imomaliev
  • 14,861
  • 2
  • 51
  • 63
  • Apologies for the delay in replying, and thanks for the answer. Unfortunately I'm still having problems so perhaps there is something else going wrong other than the email backend setting. It's not at all clear what that might be, though. – knirirr Sep 14 '16 at 16:29
  • Looks like I managed to get one email working. It seems that the User object isn't functioning properly (perhaps an issue with the fixtures). When I used your fix and sent mail in the settings without trying to load a user first I got one to pass. – knirirr Sep 14 '16 at 17:00
  • You can also use a decorator `@override_settings` which wraps around the function. That way you don't have to do ```with```. For example `from django.test import override_settings @override_settings(EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend') def test_this(TestCase): ...`. – analytical_prat Jul 22 '21 at 11:41
0

For me, I used a time.sleep(0.1) before the self.assertEquals(len(mail.outbox), 1)

E_K
  • 2,159
  • 23
  • 39