Тестирование отправки электронной почты в Django [закрыто]


90

Мне нужно проверить, что мое приложение Django отправляет электронные письма с правильным содержанием. Я не хочу полагаться на внешние системы (например, специальную учетную запись gmail ), поскольку я не тестирую реальную службу электронной почты ...

Я хотел бы, возможно, хранить электронные письма локально в папке по мере их отправки. Любой совет о том, как этого добиться?


Модераторы: заблокируйте этот вопрос. В ответы добавляется много спама, предлагая смехотворно сложные решения только для продвижения внешних сервисов.
nemesisdesign

Ответы:


43

Вы можете использовать файловый бэкэнд для отправки электронных писем, что является очень удобным решением для разработки и тестирования; электронные письма не отправляются, а хранятся в папке, которую вы можете указать!


1
Дополнительная информация о бэкэндах электронной почты: docs.djangoproject.com/en/dev/topics/email/#email-backends . Иногда бывает достаточно даже простого консольного
бэкенда

1
Но есть ли способ получить доступ к сгенерированному электронному письму во время (автоматического) тестирования?
Overdrivr

182

У тестовой среды Django есть несколько встроенных помощников, которые помогут вам при тестировании службы электронной почты .

Пример из документации (короткая версия):

from django.core import mail
from django.test import TestCase

class EmailTest(TestCase):
    def test_send_email(self):
        mail.send_mail('Subject here', 'Here is the message.',
            'from@example.com', ['to@example.com'],
            fail_silently=False)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Subject here')

3
+1 Хороший ответ. Но мне это не пригодится для сложных случаев, когда send_mailнельзя использовать.
santiagobasulto

3
Точнее, документ здесь: docs.djangoproject.com/en/1.8/topics/email/#in-memory-backend
nimiq

2
Как бы вы это сделали, если бы тестировали функцию, которая вызывает send_mail, и поэтому вы не можете получить доступ mail?
Matt D

3
@MatthewDrill вы все еще можете получить доступ, mail.outboxкогда send_mailвызывается из другой функции.
pymarco

2
@pymarco Если вы импортируете почту из ядра, mail.outbox[0].bodyвы увидите отправленное письмо, даже если send_mailоно было выполнено в другом месте.
Роб

17

Если вы занимаетесь модульным тестированием, лучшим решением будет использовать бэкэнд In-memory, предоставляемый django.

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

Возьмем случай использования его как приспособления py.test

@pytest.fixture(autouse=True)
def email_backend_setup(self, settings):
    settings.EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'  

В каждом тесте mail.outboxсбрасывается вместе с сервером, поэтому между тестами нет побочных эффектов.

from django.core import mail

def test_send(self):
    mail.send_mail('subject', 'body.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1

def test_send_again(self):
    mail.send_mail('subject', 'body.', 'from@example.com', ['to@example.com'])
    assert len(mail.outbox) == 1

8

Используйте MailHog

Вдохновленный MailCatcher, более простой в установке.

Создано с помощью Go - MailHog работает без установки на нескольких платформах.


Кроме того, у него есть компонент под названием Jim , MailHog Chaos Monkey , который позволяет вам тестировать отправку электронных писем с различными возникающими проблемами:

Что может Джим?

  • Отклонять подключения
  • Ограничение скорости соединений
  • Отклонить аутентификацию
  • Отклонять отправителей
  • Отклонить получателей

Подробнее об этом читайте здесь .


(В отличие от оригинального mailcatcher, который у меня не получился при отправке писем с эмодзи, закодированными в UTF-8, и он НЕ был исправлен в текущем выпуске, MailHog просто работает.)


5

Для любого проекта, который не требует отправки вложений, я использую django-mailer , преимущество которого заключается в том, что все исходящие электронные письма оказываются в очереди, пока я не инициирую их отправку, и даже после того, как они были отправлены, они затем регистрируются - все это видно в админке, что упрощает быструю проверку того, что вы отправляете по электронной почте, код пытается запустить в интертубах.


Кроме того, объекты сообщений, созданные django-mailer, означают, что вы можете использовать их (и проверять их содержимое) в модульных тестах (я знаю, что в наборе тестов для фиктивного почтового ящика есть поддержка исходящих почтовых ящиков, но использование django-mailer не работает). не отправлять почту, если ее не отправит команда управления, что означает, что вы не можете использовать этот объект почтового ящика)
Стив Джалим

Обновление, возраст от моего оригинального ответа: github.com/SmileyChris/django-mailer-2 тоже поддерживает вложения
Стив Джалим,

4

Django также имеет серверную часть электронной почты в памяти. Более подробная информация в документации в разделе Бэкэнд In-memory . Это присутствует в Django 1.6, не уверен, что это было раньше.



1

Связывая вместе несколько частей, вот простая установка, основанная на filebased.EmailBackend. Это отображает представление списка со ссылками на отдельные файлы журналов, которые имеют удобные временные метки для имен файлов. Щелчок по ссылке в списке отображает это сообщение в браузере (необработанное):

Настройки

EMAIL_BACKEND = "django.core.mail.backends.filebased.EmailBackend"
EMAIL_FILE_PATH = f"{MEDIA_ROOT}/email_out"

Посмотреть

import os

from django.conf import settings
from django.shortcuts import render

def mailcheck(request):

    path = f"{settings.MEDIA_ROOT}/email_out"
    mail_list = os.listdir(path)

    return render(request, "mailcheck.html", context={"mail_list": mail_list})

Шаблон

{% if mail_list %}
  <ul>
  {% for msg in mail_list %}
    <li>
      <a href="{{ MEDIA_URL }}email_out/{{msg}}">{{ msg }}</a>
    </li>
  {% endfor %}
  </ul>
{% else %}
  No messages found.
{% endif %}

URL

path("mailcheck/", view=mailcheck, name="mailcheck"),

0

Почему бы не запустить собственный действительно простой SMTP-сервер, унаследовав от smtpd.SMTPServerи threading.Thread:

class TestingSMTPServer(smtpd.SMTPServer, threading.Thread):
    def __init__(self, port=25):
        smtpd.SMTPServer.__init__(
            self,
            ('localhost', port),
            ('localhost', port),
            decode_data=False
        )
        threading.Thread.__init__(self)

    def process_message(self, peer, mailfrom, rcpttos, data, **kwargs):
        self.received_peer = peer
        self.received_mailfrom = mailfrom
        self.received_rcpttos = rcpttos
        self.received_data = data

    def run(self):
        asyncore.loop()

process_message вызывается всякий раз, когда ваш SMTP-сервер получает почтовый запрос, вы можете делать там все, что хотите.

В коде тестирования сделайте что-то вроде этого:

smtp_server = TestingSMTPServer()
smtp_server.start()
do_thing_that_would_send_a_mail()
smtp_server.close()
self.assertIn(b'hello', smtp_server.received_data)

Только не забудьте по телефону , чтобы закончить цикл asyncore (остановить сервер от прослушивания).close()asyncore.dispatchersmtp_server.close()


0

Если у вас есть сервер TomCat или другой механизм сервлетов, то хорошим подходом будет «Post Hoc», который представляет собой небольшой сервер, который выглядит для приложения точно так же, как SMTP-сервер, но включает пользовательский интерфейс, который позволяет вам просматривать и проверьте отправленные сообщения электронной почты. Это открытый исходный код и бесплатный доступ.

Найти его можно на сайте: Post Hoc GitHub

См. Сообщение в блоге: PostHoc: Тестирование приложений, отправляющих электронную почту

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.