Clean up and document Anymail's test EmailBackend

* Change Anymail's test EmailBackend to collect sent messages in
  django.core.mail.outbox, same as Django's own locmem EmailBackend.
  (So Django's test runner will automatically clear accumulated mail
  between test cases.)

* Rename EmailMessage `test_response` attr to `anymail_test_response`
  to avoid conflicts, and record merged ESP send params in
  new `anymail_send_params` attr.

* Add docs

Closes #36.
This commit is contained in:
medmunds
2017-09-01 13:13:25 -07:00
parent a9c663f36a
commit 2faa5f96cb
6 changed files with 106 additions and 42 deletions

View File

@@ -218,11 +218,13 @@ ESP send status
if you try to access it.
This might cause problems in your test cases, because Django
`substitutes its own locmem email backend`_ during testing (so anymail_status
won't be set even after sending). Your code should either guard against
a missing anymail_status attribute, or use :class:`AnymailMessage`
(or the :class:`AnymailMessageMixin`) which initializes its anymail_status
attribute to a default AnymailStatus object.
:ref:`substitutes its own locmem EmailBackend <django:topics-testing-email>`
during testing (so anymail_status never gets attached to the EmailMessage).
If you run into this, you can: change your code to guard against
a missing anymail_status attribute; switch from using EmailMessage to
:class:`AnymailMessage` (or the :class:`AnymailMessageMixin`) to ensure the
anymail_status attribute is always there; or substitute
:ref:`Anymail's test backend <test-backend>` in any affected test cases.
After sending through an Anymail backend,
:attr:`~AnymailMessage.anymail_status` will be an object with these attributes:
@@ -321,10 +323,6 @@ ESP send status
message.anymail_status.esp_response.json()
.. _substitutes its own locmem email backend:
https://docs.djangoproject.com/en/stable/topics/testing/tools/#email-services
.. _inline-images:
Inline images

View File

@@ -10,6 +10,7 @@ done with Anymail:
multiple_backends
django_templates
securing_webhooks
test_backend
.. TODO:
.. Working with django-mailer(2)

View File

@@ -0,0 +1,52 @@
.. _test-backend:
Testing your app
================
Django's own test runner makes sure your
:ref:`test cases don't send email <django:topics-testing-email>`,
by loading a dummy EmailBackend that accumulates messages
in memory rather than sending them. That works just fine with Anymail.
Anymail also includes its own "test" EmailBackend. This is intended primarily for
Anymail's own internal tests, but you may find it useful for some of your test cases, too:
* Like Django's locmem EmailBackend, Anymail's test EmailBackend collects sent messages
in :data:`django.core.mail.outbox`.
Django clears the outbox automatically between test cases.
See :ref:`email testing tools <django:topics-testing-email>` in the Django docs for more information.
* Unlike the locmem backend, Anymail's test backend processes the messages as though they
would be sent by a generic ESP. This means every sent EmailMessage will end up with an
:attr:`~anymail.message.AnymailMessage.anymail_status` attribute after sending,
and some common problems like malformed addresses may be detected.
(But no ESP-specific checks are run.)
* Anymail's test backend also adds an :attr:`anymail_send_params` attribute to each EmailMessage
as it sends it. This is a dict of the actual params that would be used to send the message,
including both Anymail-specific attributes from the EmailMessage and options that would
come from Anymail settings defaults.
Here's an example:
.. code-block:: python
from django.core import mail
from django.test import TestCase
from django.test.utils import override_settings
@override_settings(EMAIL_BACKEND='anymail.backends.test.EmailBackend')
class SignupTestCase(TestCase):
# Assume our app has a signup view that accepts an email address...
def test_sends_confirmation_email(self):
self.client.post("/account/signup/", {"email": "user@example.com"})
# Test that one message was sent:
self.assertEqual(len(mail.outbox), 1)
# Verify attributes of the EmailMessage that was sent:
self.assertEqual(mail.outbox[0].to, ["user@example.com"])
self.assertEqual(mail.outbox[0].tags, ["confirmation"]) # an Anymail custom attr
# Or verify the Anymail params, including any merged settings defaults:
self.assertTrue(mail.outbox[0].anymail_send_params["track_clicks"])