Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Django's send_mail not work during testing?

I have an app that imports a number of user email addresses and creates accounts for them. To have them set their own password, I tried to use django's PasswordResetForm (in django.contrib.auth.forms). The password reset is called as soon as a user account has been created:

def reset_password(person):
    form = PasswordResetForm({'email': person.email})
    if form.is_valid():
        form.save(from_email='[email protected]')

I haven't gotten any further with testing than including a unit test that does this:

import password_reset_module
class TestPasswordReset(TestCase):
     def setUp(self):
         p = Person(email='[email protected]')
     def test_send(self):
         password_reset_module.reset_password(p)

No assertions, right now I just want to see if there is mail sent at all by monitoring the console in which I run:

python -m smtpd -n -c DebuggingServer localhost:1025

Saving the form calls django's send_mail. When running the testcase, the send_mail method returns 1. However, no mails show up in the console. The strange thing is that calling send_mail from django's interactive shell:

python manage.py shell

works fine. Mail shows up in the console. Clicking the forgot my password link in a browser also result in sent mails.

I have also tried the file based email backend to no avail. Current settings.py email settings:

EMAIL_USE_TLS = False
EMAIL_HOST = 'localhost'
DEFAULT_FROM_EMAIL = '[email protected]'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 1025

Now wondering if I am missing something when calling the password reset, or is there a mailserver configuration issue at hands?

like image 774
glormph Avatar asked Jun 18 '13 14:06

glormph


People also ask

How does Django do testing?

The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.

How does Django implement unit testing?

Django's unit tests use a Python standard library module: unittest . This module defines tests using a class-based approach. When you run your tests, the default behavior of the test utility is to find all the test cases (that is, subclasses of unittest.

Is testing important in Django?

Testing is vital. Without properly testing your code, you will never know if the code works as it should, now or in the future when the codebase changes.


3 Answers

Your config is being overruled

In Section "Email Services", the Django testing documentation says:

Django's test runner automatically redirects all Django-sent email to a dummy outbox. [...] During test running, each outgoing email is saved in django.core.mail.outbox. This is a simple list of all EmailMessage instances that have been sent.

Huh?

The Django test runner will actually configure a different email backend for you (called locmem). It is very convenient if you want to do unit-testing only (without integration with an actual email server), but very surprising if you don't know it.

(I am not using the Django test runner manage.py test, but it happens anyway, presumably because I have pytest-django installed which magically modifies my py.test.)

If you want to override the overriding and use the email configuration given in your settings module, all you need to re-set is the setting for the email backend, e.g. like this:

@django.test.utils.override_settings(
    EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend')
def test_send_email_with_real_SMTP(self):
   ...
like image 105
Lutz Prechelt Avatar answered Oct 08 '22 15:10

Lutz Prechelt


It is probably worthwhile trying to turn this into a proper unit test, which of course you can then run as part of your automated test suite. For unit testing, probably the easiest way to check whether the mail was sent (and verify the contents of the mail if required) is to use Django's built-in in memory email backend - you can simply use the outbox attribute on this to get a list of sent mails:

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

This has the advantage of not requiring any infrastructure setup to support testing email sending, makes it very simple to assert the contents of your email, and this should also make the tests fast (when compared to actually sending the emails to an SMTP server).

Hope this helps.

like image 40
robjohncox Avatar answered Oct 08 '22 15:10

robjohncox


From what I understand, you are running the following command while testing the unit.

python -m smtpd -n -c DebuggingServer localhost:1025

This command starts up a "dumb" SMTP server that recieves your emails and displays them on the terminal. Try running your site without this DebuggingServer set up and see if the mails are sent.

Here is the reference to the docs page

like image 3
Sukrit Kalra Avatar answered Oct 08 '22 15:10

Sukrit Kalra