Testing Django email backend - django

In my settings.py, I put:
EMAIL_BACKEND = 'mailer.backend.DbBackend'
So even when importing from from django.core.mail import send_mail, the send_mail function still queues up the email in the database instead of sending it immediately.
It works just fine when actually running the website, but when testing the website, and accessing some webpages that trigger emails, emails are no longer queued anymore:
def test_something(self):
...
# Check no emails are actually sent yet
self.assertEquals(len(mail.outbox), 0) # test fails here -- 2 != 0
# Check queued emails.
messages = Message.objects.all()
self.assertEquals(messages.count(), 2) # test would also fail here -- 0 != 2
...
How come it doesn't seem to be using the backend when it is testing? (importing send_mail from mailer itself gets the tests to pass, but I can't really change the imports of other mailing apps like django-templated-email)

According to this question django overrides the setting.EMAIL_BACKEND when testing to 'django.core.mail.backends.locmem.EmailBackend'. It's also in the django docs here.

To properly test email with django-mailer, you need to override two settings:
Make the tests to use the django-mailer backend
Make the djano-mailer backend to use the test backend
If you don't set the django-mailer backend (number 2), your tests will try to send the email for real.
You also need to simulate running django-mailer's send_mail management command so that you can check mail.outbox for the correct email.
Here's an example of how to setup a test method:
from mailer.engine import send_all
#override_settings(EMAIL_BACKEND='mailer.backend.DbBackend')
#override_settings(MAILER_EMAIL_BACKEND='django.core.mail.backends.locmem.EmailBackend')
def test_email(self):
# Code that generates email goes here.
send_all() # Simulates running django-mailer's send_mail management command.
# Code to check the email in mail.outbox goes here.
This strategy makes your tests specific to django-mailer which you don't always want or need. I personally only use this setup when I'm testing specific functionality enabled by django-mailer. Otherwise, I use the default test email backend setup by django.

If you really want have sending of emails (like default) via SMTP in django tests use the decorator:
from django.test.utils import override_settings
#override_settings(EMAIL_BACKEND='django.core.mail.backends.smtp.EmailBackend')
class TestEmailVerification(TestCase):
...

Try the following:
django.core.mail.backends.console.EmailBackend

Related

Django-templated-email does not send email

I am using the following package https://github.com/vintasoftware/django-templated-email in a Django app to try and send an email. I am running it inside a django management command.
When I run the command the email part outputs to the console but no email is actually sent out. The SMTP settings and such are correct as emails work fine for other areas of the application.
from templated_email import send_templated_mail
send_templated_mail(
template_name='welcome',
from_email='from#example.com',
recipient_list=['to#example.com'],
context={
'username':"test",
'full_name':"test",
'signup_date':"test"
},
)
Any help is appreciated. I suspect I have misunderstood the syntax of the package.
Signup_date must be date and username and full_name must use single quote

does django send_mail() support custom 'from'?

my django app is sending emails (via SendGrid API) using django.core.mail's send_mail
send_mail(
subject='foo',
message=message,
from_email=settings.DEFAULT_FROM_EMAIL,
recipient_list=[user.email],
fail_silently=False,
html_message=rendered)
the emails send fine but since it's sending from hi#myapp.com the email "From" alias in inboxes just shows up as "hi", and I'd like to make it more verbose so my recipients know who sent them the email ("Hi from AppName"). I don't see any field in send_mail docs (https://docs.djangoproject.com/en/2.1/topics/email/) for customizing how the "From" sender alias string appears (beyond the email itself) other than 'The “From:” header of the email will be the value of the SERVER_EMAIL setting'. does django's send_mail not support this (i.e., I need to rewrite this to using a sendgrid lib? https://github.com/sendgrid/sendgrid-python?) or is there a config setting in SendGrid where I can automatically set 'fromname'? thanks
Is that
Your example includes from_email=settings.DEFAULT_FROM_EMAIL. The docs say that the more verbose form is permitted, for example:
from_email="Hi from Appname <hi#myapp.com>",
You might also be able to change DEFAULT_FROM_EMAIL to the more verbose form. However the docs don't say whether this is supported or not, so I'm not sure whether this would cause problems somewhere.

Integration Testing Django All-Auth Email Confirmation

I'd like to do some integration testing and I'm stuck on how I should test the email confirmation bit of the sign up process.
My user story is (basically) that a new visitor comes to the site, decides that they want to sign up, confirm their email address and then get redirected to their shiny new profile.
How would I simulate clicking on the link in the email to get redirected to the user profile?
I'm currently using Selenium to conduct my functional/integration testing and to a certain extent the django test suite.
These two documentation pages have everything you need:
https://docs.djangoproject.com/en/1.10/topics/testing/overview/
https://docs.djangoproject.com/en/1.10/topics/testing/tools/#email-services
A simple example:
from django.test import TestCase
from django.core import mail
class EmailTestCase(TestCase):
def setUp(self):
## Do something
pass
def test_email_content(self):
## Do something that triggers an email.
## Check the number of emails.
print(len(mail.outbox))
## Check the content of the first email.
first_email = mail.outbox[0]
if first_email:
print(first_email.body)
You should look up a python way to read emails.
Steps:
Find the email
Store the link
Start up your selenium test, and open up said link
Validate that you are on the page

Unit testing Django with remote calls to another server

So I have a Django app, which as part of its functionality makes a request (using the requests module) to another server. What I want to do is have a server available for unittesting which gives me canned responses to test requests from the Django app (allowing to test how Django handles the different potential responses).
An example of the code would be:
payload = {'access_key': key,
'username': name}
response = requests.get(downstream_url, params=payload)
# Handle response here ...
I've read that you can use SimpleHTTPServer to accomplish this, but I'm not sure of how I use it to this end, any thoughts would be much appreciated!
Use the mock module.
from mock import patch, MagicMock
#patch('your.module.requests')
def test_something(self, requests_mock):
response = MagicMock()
response.json.return_value = {'key': 'value'}
requests_mock.get.return_value = response
…
requests_mock.get.assert_called_once_with(…)
response.json.assert_called_once()
Much more examples in the docs.
You don't need to (and should not) test the code that makes the request. You want to mock out that part and focus on testing the logic that handles the response.

Setting HTTP_REFERER header in Django test

I'm working on a Django web application which (amongst other things) needs to handle transaction status info sent using a POST request.
In addition to the HTTP security supported by the payment gateway, my view checks request.META['HTTP_REFERER'] against an entry in settings.py to try to prevent funny business:
if request.META.get('HTTP_REFERER', '') != settings.PAYMENT_URL and not settings.DEBUG:
return HttpResponseForbidden('Incorrect source URL for updating payment status')
Now I'd like to work out how to test this behaviour.
I can generate a failure easily enough; HTTP_REFERER is (predictably) None with a normal page load:
def test_transaction_status_succeeds(self):
response = self.client.post(reverse('transaction_status'), { ... })
self.assertEqual(response.status_code, 403)
How, though, can I fake a successful submission? I've tried setting HTTP_REFERER in extra, e.g. self.client.post(..., extra={'HTTP_REFERER': 'http://foo/bar'}), but this isn't working; the view is apparently still seeing a blank header.
Does the test client even support custom headers? Is there a work-around if not? I'm using Django 1.1, and would prefer not to upgrade just yet if at all possible.
Almost right. It's actually:
def transaction_status_suceeds(self):
response = self.client.post(reverse('transaction_status'), {}, HTTP_REFERER='http://foo/bar')
I'd missed a ** (scatter operator / keyword argument unpacking operator / whatever) when reading the source of test/client.py; extra ends up being a dictionary of extra keyword arguments to the function itself.
You can pass HTTP headers to the constructor of Client:
from django.test import Client
from django.urls import reverse
client = Client(
HTTP_USER_AGENT='Mozilla/5.0',
HTTP_REFERER='http://www.google.com',
)
response1 = client.get(reverse('foo'))
response2 = client.get(reverse('bar'))
This way you don't need to pass headers every time you make a request.