Wie man eine E-Mail-Testumgebung mit einer Einweg-E-Mail-API aufbaut
Erstellen Sie eine robuste E-Mail-Testumgebung mit Einweg-E-Mail-API-Inboxes mit echtem IMAP und SMTP — umfasst Python- und Node.js-Beispiele.

E-Mail-Testen ist eines dieser Probleme, das einfach klingt und nicht ist. Ihre Anwendung sendet E-Mails — Anmeldungsbestätigungen, Passwort-Resets, Benachrichtigungen, Rechnungen. Sie müssen überprüfen, dass diese E-Mails tatsächlich ankommen, den richtigen Inhalt enthalten und sich richtig rendern. Das zuverlässig in einer automatisierten Test-Suite zu tun ist schwerer als es sein sollte.
Warum E-Mail-Testen schwer ist
Die Kernschwierigkeit: Sie können Test-E-Mails nicht an echte Benutzer senden. Nicht in CI, nicht in Staging, nie. Sie brauchen also eine Alternative.
E-Mail-Sandboxes (Mailtrap, Mailhog) fangen ausgehende E-Mails und zeigen sie in einem Dashboard. Sie sind nützlich für visuelle Inspektionen, testen aber nicht echte Zustellung. Ihre E-Mail landet tatsächlich nie in einem Posteingang. Sie können nicht IMAP-Abruf, Client-Rendering oder End-to-End-Abläufe überprüfen.
Gemeinsame Test-Konten (eine Gmail-Adresse, die das Team nutzt) schaffen flaky Tests. Rate Limits, gemeinsamer Zustand, Credential-Rotation — all dies bricht CI-Pipelines zur schlimmsten Zeit.
Der ideale Ansatz: Echte IMAP/SMTP-Inboxes, die Sie on-Demand erstellen und in Tests nutzen. Echte Zustellung, echte Protokolle, isoliert pro Test-Suite. Das ist, was eine Einweg-E-Mail-API Ihnen gibt.
Der Ansatz: Echte Inboxes für jeden Test
Mit Reusable.Email Managed Inboxes erhält jede Test-Umgebung ihr eigenes echtes E-Mail-Konto. Die Inbox hat Standard-IMAP- und SMTP-Credentials, damit Ihre Tests die gleichen Bibliotheken und Protokolle nutzen wie Ihr Production-Code.
Der Workflow:
- Erstellen Sie eine Managed Inbox (oder nutzen Sie eine für Ihre Test-Suite vorgefertigte)
- Konfigurieren Sie Ihre zu testende Anwendung, um an diese Inbox zu versenden
- Nach dem Action-Trigger verbinden Sie sich via IMAP und bestätigen den E-Mail-Inhalt
- Die Inbox verbleibt 365 Tage lang — nutzen Sie sie über Test-Runs hinweg wieder
Bei $3 pro Inbox (einmalig) sind die Kosten trivial. Zehn permanente Test-Inboxes für Ihre gesamte CI-Pipeline kosten insgesamt $30.
Python-Beispiel: E-Mail versenden und verifizieren
Dieses Beispiel simuliert einen häufigen Test-Ablauf: Ihre Anwendung sendet eine Willkommens-E-Mail und Ihr Test überprüft, dass sie mit dem richtigen Inhalt ankam.
import imaplib
import smtplib
import email
import time
from email.mime.text import MIMEText
from email.header import decode_header
# Managed Inbox Credentials
INBOX_USER = "[email protected]"
INBOX_PASS = "your-inbox-password"
IMAP_HOST = "imap.reusable.email"
SMTP_HOST = "smtp.reusable.email"
def send_test_email(to_address, subject, body):
"""Versenden Sie eine E-Mail via SMTP (simuliert Ihre App, die eine Benachrichtigung sendet)."""
msg = MIMEText(body)
msg["Subject"] = subject
msg["From"] = INBOX_USER
msg["To"] = to_address
with smtplib.SMTP(SMTP_HOST, 587) as server:
server.starttls()
server.login(INBOX_USER, INBOX_PASS)
server.send_message(msg)
def wait_for_email(subject_contains, timeout=30):
"""Poll IMAP, bis eine E-Mail mit matching subject ankommt."""
imap = imaplib.IMAP4_SSL(IMAP_HOST, 993)
imap.login(INBOX_USER, INBOX_PASS)
start = time.time()
while time.time() - start < timeout:
imap.select("INBOX")
status, messages = imap.search(None, "UNSEEN")
for msg_id in messages[0].split():
status, msg_data = imap.fetch(msg_id, "(RFC822)")
msg = email.message_from_bytes(msg_data[0][1])
subject = decode_header(msg["Subject"])[0][0]
if isinstance(subject, bytes):
subject = subject.decode()
if subject_contains.lower() in subject.lower():
imap.logout()
return msg
time.sleep(2)
imap.logout()
raise TimeoutError(f"Keine E-Mail matching '{subject_contains}' in {timeout}s")
# --- Test flow ---
send_test_email(INBOX_USER, "Willkommen in unserer App", "Danke für die Anmeldung!")
received = wait_for_email("Willkommen in unserer App")
assert "Danke für die Anmeldung!" in received.get_payload(decode=True).decode()
print("Test passed: Willkommens-E-Mail empfangen und überprüft.")
Dies ist ein echter End-to-End-Test. Die E-Mail wird tatsächlich via SMTP versendet, wirklich zugestellt und tatsächlich über IMAP gelesen. Keine Mocks, keine Sandboxes.
Node.js-Beispiel: Versenden und zurück lesen
Das gleiche Muster in Node.js, nutze nodemailer zum Versenden und imapflow zum Empfangen:
const nodemailer = require("nodemailer");
const { ImapFlow } = require("imapflow");
const INBOX_USER = "[email protected]";
const INBOX_PASS = "your-inbox-password";
// Versenden Sie eine E-Mail via SMTP
async function sendEmail(subject, body) {
const transporter = nodemailer.createTransport({
host: "smtp.reusable.email",
port: 587,
secure: false,
auth: { user: INBOX_USER, pass: INBOX_PASS },
});
await transporter.sendMail({
from: INBOX_USER,
to: INBOX_USER,
subject,
text: body,
});
}
// Poll IMAP für eine matching E-Mail
async function waitForEmail(subjectContains, timeoutMs = 30000) {
const client = new ImapFlow({
host: "imap.reusable.email",
port: 993,
secure: true,
auth: { user: INBOX_USER, pass: INBOX_PASS },
});
await client.connect();
const start = Date.now();
while (Date.now() - start < timeoutMs) {
const lock = await client.getMailboxLock("INBOX");
try {
for await (const message of client.fetch({ seen: false }, { source: true })) {
const parsed = require("mailparser").simpleParser;
const mail = await parsed(message.source);
if (mail.subject && mail.subject.includes(subjectContains)) {
await client.logout();
return mail;
}
}
} finally {
lock.release();
}
await new Promise((r) => setTimeout(r, 2000));
}
await client.logout();
throw new Error(`Keine E-Mail matching '${subjectContains}' within timeout`);
}
// --- Test flow ---
(async () => {
await sendEmail("Passwort Reset", "Ihr Reset-Code ist 123456");
const mail = await waitForEmail("Passwort Reset");
console.assert(mail.text.includes("123456"), "Reset-Code sollte im E-Mail-Body sein");
console.log("Test passed: Passwort-Reset-E-Mail überprüft.");
})();
CI/CD Integration
Für kontinuierliche Integration sind die Schlüsselvons:
Pre-erstellen Sie Inboxes, nicht pro-Durchlauf. Da Managed Inboxes permanent (365-Tage-Aufbewahrung) sind und einmalig $3 kosten, erstellen Sie eine Set von Test-Inboxes voraus und speichern Sie die Credentials als CI-Secrets. Dies vermeidet, API-Aufrufe zum Erstellen von Inboxes während der Pipeline zu brauchen.
Eine Inbox pro Test-Suite, nicht pro Test. Wenn Ihre Tests nicht konfliktäre E-Mails an die gleiche Adresse versenden, funktioniert eine einzelne Inbox pro Suite. Nutzen Sie eindeutige Subjects oder Message-IDs zum Unterscheiden zwischen Test-E-Mails.
Bereinigen Sie zwischen Durchläufen. Bevor Sie einen Test-Durchlauf starten, entweder markieren Sie alle existierenden Nachrichten als gelesen oder löschen Sie sie via IMAP. Dies verhindert stale E-Mails, die falsch-positive verursachen.
# Bereinige Inbox vor Test-Durchlauf
def clean_inbox():
imap = imaplib.IMAP4_SSL("imap.reusable.email", 993)
imap.login(INBOX_USER, INBOX_PASS)
imap.select("INBOX")
status, messages = imap.search(None, "ALL")
for msg_id in messages[0].split():
imap.store(msg_id, "+FLAGS", "\\Seen")
imap.logout()
Speichern Sie Credentials sicher. Behandeln Sie Inbox-Credentials wie jedes andere Secret in CI — nutzen Sie Umgebungsvariablen, nicht hart-codierte Werte.
Handhabe Timeouts elegant. E-Mail-Zustellung kann ein paar Sekunden dauern. Setzen Sie Ihren Polling-Timeout hoch genug, um flaky Tests zu vermeiden (30 Sekunden ist ein angemessenes Standard), aber nicht so hoch, dass eine genuinely fehlende E-Mail Ihre Pipeline für Minuten aufhängt.
Framework-Integration
Die meisten Test-Frameworks unterstützen Setup/Teardown Hooks, die die Inbox-Verwaltung sauber machen:
# pytest Beispiel
import pytest
@pytest.fixture(autouse=True)
def clean_test_inbox():
"""Markieren Sie alle E-Mails vor jedem Test als gelesen."""
imap = imaplib.IMAP4_SSL("imap.reusable.email", 993)
imap.login(INBOX_USER, INBOX_PASS)
imap.select("INBOX")
status, messages = imap.search(None, "ALL")
for msg_id in messages[0].split():
imap.store(msg_id, "+FLAGS", "\\Seen")
imap.logout()
yield
# Teardown: nichts erforderlich, E-Mails verbleiben für Nächste Durchlauf-Referenz
def test_welcome_email(app_client):
"""Überprüfen Sie, dass Anmeldung eine Willkommens-E-Mail sendet."""
app_client.post("/signup", json={"email": INBOX_USER, "name": "Test"})
imap = imaplib.IMAP4_SSL("imap.reusable.email", 993)
imap.login(INBOX_USER, INBOX_PASS)
msg = wait_for_email(imap, "Welcome")
body = msg.get_payload(decode=True).decode()
assert "Welcome" in msg["Subject"]
assert "Test" in body
imap.logout()
Kostenanalyse
| Szenario | Inboxes | Kosten |
|---|---|---|
| Klein-Projekt, einzelne Test-Inbox | 1 | $3 (einmalig) |
| Team CI mit Suite-pro-Service | 5 | $15 (einmalig) |
| Vollständige Test-Matrix (dev, staging, CI) | 10 | $30 (einmalig) |
| Große Org, Pro-Team-Inboxes | 50 | $150 (einmalig) |
Vergleichen Sie das mit einem Sandbox-Dienst bei $15-35/Monat ($180-420/Jahr) und die Wirtschaftlichkeit ist klar. Die Test-Inboxes, die Sie heute erstellen, werden in einem Jahr immer noch funktionieren, ohne Erneuerung.
Für Teams, die programmatische Inbox-Erstellung brauchen (Hunderte von Inboxes, Pro-Test-Isolierung), includes die Whitelabel-Ebene auf $30/Monat unbegrenzte Inbox-Erstellung via API.
Debugging gescheiterter E-Mail-Tests
Wenn ein E-Mail-Test fehlschlägt, ist die Ursache normalerweise eine von diesen:
E-Mail ist noch nicht angekommen. Erhöhen Sie Ihren Polling-Timeout. SMTP-Zustellung ist nicht sofort — Nachrichten können ein paar Sekunden dauern, um die Pipeline zu durchqueren. Ein 30-Sekunden-Timeout ist für die meisten Setups angemessen.
Falsche Inbox. Doppel-überprüfen Sie, dass Ihre Anwendung an die gleiche Adresse versendet, die Ihr Test pollt. Ein häufiger Fehler ist, die App zum Versenden an eine andere Adresse zu konfigurieren als die, mit der Ihr IMAP-Code sich verbindet.
E-Mail war bereits gelesen. Falls ein vorheriger Test-Durchlauf (oder ein früherer Test im gleichen Durchlauf) die E-Mail bereits als gesehen markiert hat, findet Ihre UNSEEN-Suche sie nicht. Nutzen Sie das Cleanup-Fixture oben oder suchen Sie nach Nachrichten nach Subject und Datum, anstatt sich ausschließlich auf das unseen-Flag zu verlassen.
SMTP-Authentifizierung fehlgeschlagen. Überprüfen Sie, dass Ihre SMTP-Credentials zu den Managed Inbox Credentials passen. Überprüfen Sie, dass Sie Port 587 mit STARTTLS nutzen, nicht Port 465 mit implizitem SSL.
Firewall- oder Netzwerk-Einschränkungen. Einige CI-Umgebungen beschränken ausgehende Verbindungen. Stellen Sie sicher, dass Port 587 (SMTP) und 993 (IMAP) in Ihrem CI-Provider-Netzwerk-Konfiguration zulässig sind.
Was weiter
Dieser Ansatz gibt Ihnen echtes E-Mail-Testen mit Standard-Protokollen. Für mehr auf den breiteren Developer-Anwendungsfällen — Staging-Isolierung, Pro-Benutzer-Inboxes, E-Mail-Produkte bauen — siehe E-Mail-API für Entwickler-Leitfaden.
Für Details zur Konfiguration von SMTP speziell, einschließlich wann gefälschte SMTP-Server mehr Sinn machen als echte Zustellung, lesen Sie SMTP-Testen: Test ausgehende E-Mails, ohne an echte Inboxes zu versenden.
Und wenn Sie die Landschaft von Einweg-E-Mail-Diensten breiter evaluieren, deckt dieser Leitfaden das volle Spektrum von kostenlosen öffentlichen Inboxes zu verwalteten Konten ab.
Try it free
Get a disposable inbox in seconds
No sign-up required. Just visit an address and it's live. Works with any domain on reusable.email.
Open your inbox →

