Why does this Django test pass?

2020-07-23 04:32发布

问题:

Calling the send_mail function independently will cause a BadHeaderError exception due to the newline in the subject.

I expect this test_newline_causes_exception to fail as well, but it does not. This is in Django 1.3. Any ideas?

from django.core.mail import send_mail
from django.utils import unittest

class EmailTestCase(unittest.TestCase):

    def test_newline_causes_exception(self):
        send_mail('Header\nInjection', 'Here is the message.', 'from@example.com',
                  ['to@example.com'], fail_silently=False)

EDIT: This new test shows that the header checking code (django.core.mail.message.forbid_multi_line_headers) is not called when send_mail is used in tests.

from django.core.mail import send_mail, BadHeaderError, outbox
from django.utils import unittest

class EmailTestCase(unittest.TestCase):

    def test_newline_in_subject_should_raise_exception(self):

        try:
            send_mail('Subject\nhere', 'Here is the message.',
                      'from@example.com', ['to@example.com'], fail_silently=False)
        except BadHeaderError:
            raise Exception

        self.assertEqual(len(outbox), 1)

        self.assertEqual(outbox[0].subject, 'Subject here')

Result:

AssertionError: 'Subject\nhere' != 'Subject here'

回答1:

You're not really testing anything. Testing would imply checking if the BadHeaderError has been raised or not. The test would fail if an assert test is false. You could do something like this -

def test_newline_causes_exception(self)
    error_occured = False
    try:
        send_mail('Header\nInjection', 'Here is the message.', 'from@example.com',
                  ['to@example.com'], fail_silently=False)
    except BadHeaderError:
        error_occured = True

    self.assertTrue(error_ocurred)

I haven't tested it. But it should work.

PS: from django.core.mail import send_mail, BadHeaderError



回答2:

I found that this issue has been fixed in Django 1.5. The testing email backend (locmem.py) now performs the same header sanitization as the standard backends.

https://code.djangoproject.com/ticket/18861

https://github.com/django/django/commit/8599f64e54adfb32ee6550ed7a6ec9944034d978

EDIT

I found a workaround for testing header validation in Django versions <1.5.

Use the get_connection method to load the console backend which performs the same validations as the production backend.

Thanks to Alexander Afanasiev for pointing me in the right direction.

connection = get_connection('django.core.mail.backends.console.EmailBackend')
send_mail('Subject\nhere',
          'Here is the message.',
          'from@example.com',
          ['to@example.com'],
          fail_silently=False,
          connection=connection)