E-Mail-Versand in Django testen [geschlossen]


87

Ich muss testen, ob meine Django-Anwendung E-Mails mit korrektem Inhalt sendet. Ich möchte mich nicht auf externe Systeme verlassen (wie ein Ad-hoc- Google Mail- Konto), da ich den eigentlichen E-Mail-Dienst nicht teste ...

Ich möchte die E-Mails möglicherweise lokal in einem Ordner speichern, während sie gesendet werden. Irgendwelche Tipps, wie man es erreicht?


Moderatoren: Bitte sperren Sie diese Frage. In den Antworten wird viel Spam hinzugefügt, und es werden Lösungen vorgeschlagen, die lächerlich komplex sind, nur um externe Dienste zu fördern.
Nemesisdesign

Antworten:


43

Sie können ein Datei-Backend zum Senden von E-Mails verwenden. Dies ist eine sehr praktische Lösung für die Entwicklung und das Testen. E-Mails werden nicht gesendet, sondern in einem Ordner gespeichert, den Sie angeben können!


1
Weitere Informationen zu E-Mail-Backends: docs.djangoproject.com/de/dev/topics/email/#email-backends . Manchmal reicht sogar ein einfaches Konsolen-Backend aus.
Jeewes

1
Aber gibt es eine Möglichkeit, während (automatisierter) Tests auf die generierte E-Mail zuzugreifen?
Overdrivr

180

Das Django-Testframework verfügt über einige integrierte Hilfsprogramme, die Sie beim Testen des E-Mail-Dienstes unterstützen .

Beispiel aus Dokumenten (Kurzversion):

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 Gute Antwort. Aber ich es ist nicht nützlich für komplexe Fälle, wenn send_mailnicht verwendet werden kann.
Santiagobasulto


2
Wie würden Sie dies tun, wenn Sie eine Funktion testen, die send_mail aufruft und auf die Sie daher nicht zugreifen können mail?
Matt D

3
@MatthewDrill Sie können immer noch darauf zugreifen, mail.outboxwenn send_mailes in einer anderen Funktion aufgerufen wird.
Pymarco

2
@pymarco Wenn Sie mail.outbox[0].bodyE- Mails aus dem Core importieren, wird Ihnen die gesendete E- Mail angezeigt, auch wenn diese an einer send_mailanderen Stelle ausgeführt wird.
Rob

15

Wenn Sie sich für Unit-Tests interessieren, ist die beste Lösung die Verwendung des von django bereitgestellten In-Memory-Backends .

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

Nehmen Sie den Fall der Verwendung als py.test- Vorrichtung

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

Bei jedem Test wird das mail.outboxmit dem Server zurückgesetzt, sodass zwischen den Tests keine Nebenwirkungen auftreten.

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

Verwenden Sie MailHog

Inspiriert von MailCatcher, einfacher zu installieren.

Mit Go - MailHog erstellt, läuft ohne Installation auf mehreren Plattformen.


Außerdem enthält es eine Komponente namens Jim , den MailHog Chaos Monkey , mit der Sie das Senden von E-Mails mit verschiedenen Problemen testen können:

Was kann Jim tun?

  • Verbindungen ablehnen
  • Ratenbegrenzungsverbindungen
  • Authentifizierung ablehnen
  • Absender ablehnen
  • Empfänger ablehnen

Lesen Sie hier mehr darüber .


(Im Gegensatz zum ursprünglichen Mailcatcher, der beim Senden von E-Mails mit Emoji fehlgeschlagen ist, in UTF-8 codiert und in der aktuellen Version NICHT wirklich behoben wurde, funktioniert MailHog einfach.)


5

Für jedes Projekt, bei dem keine Anhänge gesendet werden müssen, verwende ich gesendet werden müssen django-mailer. Dies hat den Vorteil, dass alle ausgehenden E-Mails in einer Warteschlange landen, bis ich sie sende, und selbst nachdem sie gesendet wurden, werden sie protokolliert. All dies ist im Admin sichtbar, sodass Sie schnell überprüfen können, welchen E-Mail-Code Sie in die Intertubes abfeuern möchten.


Darüber hinaus können Sie mit den von django-mailer erstellten Nachrichtenobjekten diese auch in Komponententests bearbeiten (und deren Inhalt überprüfen) (ich weiß, dass in der Testsuite Unterstützung für ausgehende Postfächer für ein Dummy-Postfach vorhanden ist, Django-Mailer jedoch nicht Senden Sie keine E-Mails, es sei denn, der Verwaltungsbefehl sendet sie, was bedeutet, dass Sie dieses Postfachobjekt nicht verwenden können.)
Steve Jalim

Update, Alter von meiner ursprünglichen Antwort: github.com/SmileyChris/django-mailer-2 unterstützt auch Anhänge
Steve Jalim

4

Django hat auch ein In-Memory-E-Mail-Backend. Weitere Details finden Sie in den Dokumenten unter In-Memory-Backend . Dies ist in Django 1.6 nicht sicher, ob es in etwas früher vorhanden ist.



1

Wenn Sie einige der Teile hier zusammenbinden, finden Sie hier ein einfaches Setup, das auf basiert filebased.EmailBackend. Dadurch wird eine Listenansicht erstellt, die mit den einzelnen Protokolldateien verknüpft ist, deren Dateinamen bequem mit einem Zeitstempel versehen sind. Durch Klicken auf einen Link in der Liste wird diese Meldung im Browser angezeigt (roh):

die Einstellungen

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

Aussicht

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

Vorlage

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

URLs

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

0

Warum starten Sie nicht Ihren eigenen wirklich einfachen SMTP-Server, indem Sie von smtpd.SMTPServerund erben 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 wird immer dann aufgerufen, wenn Ihr SMTP-Server eine E-Mail-Anfrage erhält. Sie können dort tun, was Sie wollen.

Gehen Sie im Testcode folgendermaßen vor:

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

Denken Sie daran, close()die asyncore.dispatcherdurch den Aufruf smtp_server.close()der asyncore Schleife zu beenden (stoppen Sie den Server von Hören).


0

Wenn Sie einen TomCat-Server oder eine andere Servlet-Engine zur Verfügung haben, ist "Post Hoc" ein guter Ansatz, bei dem es sich um einen kleinen Server handelt, der für die Anwendung genau wie ein SMTP-Server aussieht, der jedoch eine Benutzeroberfläche enthält, mit der Sie und anzeigen können Überprüfen Sie die gesendeten E-Mail-Nachrichten. Es ist Open Source und frei verfügbar.

Finden Sie es unter: Post Hoc GitHub Site

Siehe den Blog-Beitrag: PostHoc: Testen von Apps, die E-Mails senden

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.