Skip to content

Commit b402c5d

Browse files
peterlauripelme
authored andcommitted
Adding mailoutbox fixture, and removing internal _django_clear_outbox (#410)
1 parent 54650ea commit b402c5d

File tree

5 files changed

+112
-16
lines changed

5 files changed

+112
-16
lines changed

docs/changelog.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
11
Changelog
22
=========
33

4+
3.1.0
5+
-----
6+
7+
Features
8+
^^^^^^^^
9+
* Added new function scoped fixture ``mailoutbox`` that gives access to
10+
djangos ``mail.outbox``. The will clean/empty the ``mail.outbox`` to
11+
assure that no old mails are still in the outbox.
12+
13+
Compatibility
14+
^^^^^^^^^^^^^
15+
* IMPORTANT: the internal autouse fixture _django_clear_outbox has been
16+
removed. If you have relied on this to get an empty outbox for your
17+
test, you should change tests to use the ``mailoutbox`` fixture instead.
18+
See documentation of ``mailoutbox`` fixture for usage. If you try to
19+
access mail.outbox directly, AssertionError will be raised.
20+
421
3.0.0
522
-----
623

docs/helpers.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,3 +217,25 @@ Example
217217
def test_with_specific_settings(settings):
218218
settings.USE_TZ = True
219219
assert settings.USE_TZ
220+
221+
``mailoutbox``
222+
~~~~~~~~~~~~~~~~~~~~~~~~~
223+
224+
A clean mail outbox where django emails are being sent.
225+
226+
Example
227+
"""""""
228+
229+
::
230+
231+
from django.core import mail
232+
233+
def test_mail(mailoutbox):
234+
mail.send_mail('subject', 'body', '[email protected]', ['[email protected]'])
235+
assert len(mailoutbox) == 1
236+
m = mailoutbox[0]
237+
assert m.subject == 'subject'
238+
assert m.body == 'body'
239+
assert m.from_email == '[email protected]'
240+
assert list(m.to) == ['[email protected]']
241+

pytest_django/plugin.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -399,12 +399,36 @@ def teardown():
399399
request.addfinalizer(teardown)
400400

401401

402-
@pytest.fixture(autouse=True, scope='function')
403-
def _django_clear_outbox(django_test_environment):
404-
"""Clear the django outbox, internal to pytest-django."""
405-
if django_settings_is_configured():
406-
from django.core import mail
407-
del mail.outbox[:]
402+
class _DirectMailboxAccessProtector(list):
403+
404+
def _raise_assertion(*args, **kwargs):
405+
raise AssertionError('To access mail.outbox, use the mailoutbox fixture.')
406+
407+
__len__ = __getitem__ = __nonzero__ = __bool__ = _raise_assertion
408+
409+
410+
@pytest.fixture(autouse=True)
411+
def _error_on_direct_mail_outbox_access(monkeypatch):
412+
if not django_settings_is_configured():
413+
return
414+
415+
from django.core import mail
416+
417+
outbox = _DirectMailboxAccessProtector()
418+
monkeypatch.setattr(mail, 'outbox', outbox)
419+
return outbox
420+
421+
422+
@pytest.fixture(scope='function')
423+
def mailoutbox(monkeypatch, _error_on_direct_mail_outbox_access):
424+
if not django_settings_is_configured():
425+
return
426+
427+
from django.core import mail
428+
429+
outbox = list()
430+
monkeypatch.setattr(mail, 'outbox', outbox)
431+
return outbox
408432

409433

410434
@pytest.fixture(autouse=True, scope='function')

tests/test_environment.py

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import pytest
66
from django.core import mail
77
from django.db import connection
8+
from django.test import TestCase
89

910
from pytest_django_test.app.models import Item
1011

@@ -15,19 +16,34 @@
1516
# to do it.
1617

1718

18-
def test_mail():
19-
assert len(mail.outbox) == 0
19+
def test_direct_mailbox_access_not_allowed():
20+
with pytest.raises(AssertionError):
21+
len(mail.outbox)
22+
23+
with pytest.raises(AssertionError):
24+
mail.outbox[0]
25+
26+
with pytest.raises(AssertionError):
27+
if mail.outbox:
28+
pass
29+
30+
31+
def test_direct_mailbox_proection_should_not_break_sending_mail():
2032
mail.send_mail('subject', 'body', '[email protected]', ['[email protected]'])
21-
assert len(mail.outbox) == 1
22-
m = mail.outbox[0]
23-
assert m.subject == 'subject'
24-
assert m.body == 'body'
25-
assert m.from_email == '[email protected]'
26-
assert list(m.to) == ['[email protected]']
2733

2834

29-
def test_mail_again():
30-
test_mail()
35+
class TestDirectAccessWorksForDjangoTestCase(TestCase):
36+
37+
def _do_test(self):
38+
assert len(mail.outbox) == 0
39+
mail.send_mail('subject', 'body', '[email protected]', ['[email protected]'])
40+
assert len(mail.outbox) == 1
41+
42+
def test_one(self):
43+
self._do_test()
44+
45+
def test_two(self):
46+
self._do_test()
3147

3248

3349
@pytest.mark.django_project(extra_settings="""

tests/test_fixtures.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from django.db import connection
1212
from django.conf import settings as real_settings
13+
from django.core import mail
1314
from django.test.client import Client, RequestFactory
1415
from django.test.testcases import connections_support_transactions
1516
from django.utils.encoding import force_text
@@ -403,3 +404,19 @@ def test_unblock_manually(self, django_db_blocker):
403404
def test_unblock_with_block(self, django_db_blocker):
404405
with django_db_blocker.unblock():
405406
Item.objects.exists()
407+
408+
409+
def test_mail(mailoutbox):
410+
assert mailoutbox is mail.outbox # check that mail.outbox and fixture value is same object
411+
assert len(mailoutbox) == 0
412+
mail.send_mail('subject', 'body', '[email protected]', ['[email protected]'])
413+
assert len(mailoutbox) == 1
414+
m = mailoutbox[0]
415+
assert m.subject == 'subject'
416+
assert m.body == 'body'
417+
assert m.from_email == '[email protected]'
418+
assert list(m.to) == ['[email protected]']
419+
420+
421+
def test_mail_again(mailoutbox):
422+
test_mail(mailoutbox)

0 commit comments

Comments
 (0)