Testowanie wysyłania e-maili w Django [zamknięte]


90

Muszę sprawdzić, czy moja aplikacja Django wysyła e-maile z poprawną zawartością. Nie chcę polegać na systemach zewnętrznych (takich jak ad-hoc konto Gmail ), ponieważ nie testuję samej usługi e-mail ...

Być może chciałbym przechowywać e-maile lokalnie, w folderze, gdy są wysyłane. Masz jakąś wskazówkę, jak to osiągnąć?


Moderatorzy: zablokujcie to pytanie. W odpowiedziach pojawia się dużo spamu, proponując rozwiązania, które są absurdalnie złożone tylko w celu promowania usług zewnętrznych.
nemesisdesign

Odpowiedzi:


43

Możesz użyć zaplecza plików do wysyłania wiadomości e-mail, co jest bardzo wygodnym rozwiązaniem do programowania i testowania; wiadomości e-mail nie są wysyłane, ale przechowywane w folderze, który możesz określić!


1
Więcej informacji na temat backendów poczty e-mail: docs.djangoproject.com/en/dev/topics/email/#email-backends . Czasami nawet prosty backend konsoli jest wystarczający ...
Jeewes

1
Ale czy istnieje sposób uzyskania dostępu do wygenerowanej wiadomości e-mail podczas (zautomatyzowanych) testów?
Overdrivr

182

Framework testowy Django ma wbudowanych pomocników, które pomogą Ci w testowaniu usługi e-mail .

Przykład z dokumentów (wersja skrócona):

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 Dobra odpowiedź. Ale ja nie jest to przydatne w skomplikowanych przypadkach, kiedy send_mailnie można go użyć.
santiagobasulto


2
Jak byś to zrobił, jeśli testujesz funkcję, która wywołuje send_mail i dlatego nie masz do niej dostępu mail?
Matt D

3
@MatthewDrill, do którego nadal możesz uzyskać dostęp, mail.outboxgdy send_mailzostanie wywołana w innej funkcji.
pymarco

2
@pymarco Jeśli importujesz pocztę z rdzenia, mail.outbox[0].bodywyświetli wysłaną wiadomość e-mail, nawet jeśli send_mailjest to wykonywane gdzie indziej.
Rob

17

Jeśli interesujesz się testowaniem jednostkowym, najlepszym rozwiązaniem jest użycie zaplecza In-memory dostarczanego przez django.

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

Weź przypadek użycia go jako urządzenia py.test

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

W każdym teście mail.outboxjest resetowany z serwerem, więc między testami nie ma żadnych skutków ubocznych.

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

Użyj MailHog

Zainspirowany MailCatcherem, łatwiejszy w instalacji.

Zbudowany z Go - MailHog działa bez instalacji na wielu platformach.


Zawiera również komponent o nazwie Jim , MailHog Chaos Monkey , który umożliwia testowanie wysyłania wiadomości e-mail z różnymi problemami:

Co Jim może zrobić?

  • Odrzuć połączenia
  • Połączenia z limitem szybkości
  • Odrzuć uwierzytelnienie
  • Odrzuć nadawców
  • Odrzuć odbiorców

Przeczytaj więcej na ten temat tutaj .


(W przeciwieństwie do oryginalnego Mailcatchera, który zawiódł mnie podczas wysyłania e-maili z emoji, zakodowany w UTF-8 i NIE został naprawiony w bieżącej wersji, MailHog po prostu działa.)


5

W przypadku każdego projektu, który nie wymaga wysyłania załączników, używam django-mailer , który ma tę zaletę, że wszystkie wychodzące wiadomości e-mail trafiają do kolejki, dopóki nie wyzwolę ich wysłania, a nawet po ich wysłaniu są następnie rejestrowane - wszystko to jest widoczne w panelu administracyjnym, co ułatwia szybkie sprawdzenie, jaki kod e-mailowy próbuje odpalić w intertubach.


Co więcej, obiekty Message utworzone przez django-mailer oznaczają, że możesz je prodować (i sprawdzać ich zawartość) również w testach jednostkowych (wiem, że w zestawie testowym istnieje obsługa poczty wychodzącej dla fikcyjnej skrzynki pocztowej, ale użycie django-mailer nie nie wysyłaj poczty, chyba że wyśle ​​ją polecenie zarządzania, co oznacza, że ​​nie możesz użyć tego obiektu skrzynki pocztowej)
Steve Jalim

Aktualizacja, wieki od mojej oryginalnej odpowiedzi: github.com/SmileyChris/django-mailer-2 obsługuje również załączniki
Steve Jalim

4

Django ma również zaplecze e-mail w pamięci. Więcej szczegółów w dokumentach w sekcji Backend w pamięci . Jest to obecne w Django 1.6, nie jestem pewien, czy jest obecne w czymkolwiek wcześniej.



1

Łącząc kilka elementów razem, oto prosta konfiguracja oparta na filebased.EmailBackend . Powoduje to wyświetlenie listy zawierającej łącza do poszczególnych plików dziennika, które mają wygodne nazwy plików ze znacznikami czasu. Kliknięcie łącza na liście powoduje wyświetlenie tego komunikatu w przeglądarce (nieprzetworzone):

Ustawienia

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

Widok

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})

Szablon

{% 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 %}

adresy URL

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

0

Dlaczego nie uruchomić własnego, naprawdę prostego serwera SMTP, dziedzicząc po smtpd.SMTPServeri 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()

komunikat_procesu jest wywoływany za każdym razem, gdy serwer SMTP otrzyma żądanie poczty, możesz tam robić, co chcesz.

W kodzie testowym zrób coś takiego:

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

Wystarczy pamiętać, aby dzwoniąc do końca pętli asyncore (zatrzymać serwer ze słuchania).close()asyncore.dispatchersmtp_server.close()


0

Jeśli masz dostępny serwer TomCat lub inny silnik serwletów, dobrym podejściem jest „Post Hoc”, który jest małym serwerem, który wygląda dla aplikacji dokładnie tak, jak serwer SMTP, ale zawiera interfejs użytkownika, który umożliwia przeglądanie i sprawdź wysłane wiadomości e-mail. Jest open source i jest ogólnie dostępny.

Znajdziesz go na: Post Hoc GitHub Site

Zobacz wpis na blogu: PostHoc: Testowanie aplikacji, które wysyłają e-maile

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.