Comment construire un environnement de test e-mail avec une API e-mail jetable
Construisez un environnement de test e-mail robuste en utilisant les boîtes de réception API e-mail jetable avec vrai IMAP et SMTP — inclut les exemples Python et Node.js.

Le test des e-mails est l'un de ces problèmes qui semble simple et ne l'est pas. Votre application envoie des e-mails — confirmations d'inscription, réinitialisations de mot de passe, notifications, factures. Vous avez besoin de vérifier que ces e-mails arrivent réellement, contiennent le bon contenu et s'affichent correctement. Faire cela de manière fiable dans une suite de tests automatisés est plus difficile qu'il ne devrait l'être.
Pourquoi le test des e-mails est difficile
La difficulté principale : vous ne pouvez pas envoyer d'e-mails de test aux utilisateurs réels. Pas en CI, pas en staging, jamais. Vous avez donc besoin d'une alternative.
Les bacs à sable e-mail (Mailtrap, Mailhog) capturent les e-mails sortants et les affichent dans un tableau de bord. Ils sont utiles pour l'inspection visuelle, mais ils ne testent pas la livraison réelle. Votre e-mail ne finit jamais vraiment dans une boîte de réception. Vous ne pouvez pas vérifier la récupération IMAP, le rendu du client ou les flux de bout en bout.
Les comptes de test partagés (une adresse Gmail que l'équipe utilise) créent des tests instables. Les limites de débit, l'état partagé, la rotation des identifiants — tout cela casse les pipelines CI au pire moment possible.
L'approche idéale : des boîtes de réception IMAP/SMTP réelles que vous créez à la demande et utilisez dans les tests. Une vraie livraison, des vrais protocoles, isolés par suite de tests. C'est ce qu'une API e-mail jetable vous donne.
L'approche : des boîtes de réception réelles pour chaque test
Avec les boîtes de réception gérées Reusable.Email, chaque environnement de test obtient son propre compte e-mail réel. La boîte de réception a les identifiants IMAP et SMTP standard, afin que vos tests utilisent les mêmes bibliothèques et protocoles que votre code de production.
Le flux de travail :
- Créez une boîte de réception gérée (ou utilisez-en une pré-créée pour votre suite de tests)
- Configurez votre application en test pour envoyer à cette boîte de réception
- Après que l'action soit déclenchée, connectez-vous via IMAP et affirmez le contenu de l'e-mail
- La boîte de réception persiste pendant 365 jours — réutilisez-la à travers les exécutions de test
À 3 $ par boîte de réception (une seule fois), le coût est trivial. Dix boîtes de réception de test permanentes pour l'ensemble de votre pipeline CI coûtent 30 $ au total.
Exemple Python : envoyer et vérifier un e-mail
Cet exemple simule un flux de test courant : votre application envoie un e-mail de bienvenue et votre test vérifie qu'il est arrivé avec le bon contenu.
import imaplib
import smtplib
import email
import time
from email.mime.text import MIMEText
from email.header import decode_header
# Identifiants de boîte de réception gérée
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):
"""Envoyez un e-mail via SMTP (simule une notification d'application)."""
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):
"""Sondez IMAP jusqu'à ce qu'un e-mail avec un sujet correspondant arrive."""
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"No email matching '{subject_contains}' within {timeout}s")
# --- Flux de test ---
send_test_email(INBOX_USER, "Welcome to Our App", "Thanks for signing up!")
received = wait_for_email("Welcome to Our App")
assert "Thanks for signing up!" in received.get_payload(decode=True).decode()
print("Test passed: welcome email received and verified.")
C'est un vrai test de bout en bout. L'e-mail est vraiment envoyé via SMTP, vraiment livré et vraiment lu via IMAP. Pas de mocks, pas de bacs à sable.
Exemple Node.js : envoyer et relire
Le même modèle en Node.js, en utilisant nodemailer pour l'envoi et imapflow pour la réception :
const nodemailer = require("nodemailer");
const { ImapFlow } = require("imapflow");
const INBOX_USER = "[email protected]";
const INBOX_PASS = "your-inbox-password";
// Envoyer un 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,
});
}
// Sondez IMAP pour un e-mail correspondant
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(`No email matching '${subjectContains}' within timeout`);
}
// --- Flux de test ---
(async () => {
await sendEmail("Password Reset", "Your reset code is 123456");
const mail = await waitForEmail("Password Reset");
console.assert(mail.text.includes("123456"), "Reset code should be in email body");
console.log("Test passed: password reset email verified.");
})();
Intégration CI/CD
Pour l'intégration continue, les décisions clés sont :
Pré-créez les boîtes de réception, ne les créez pas par exécution. Puisque les boîtes de réception gérées sont permanentes (conservation de 365 jours) et coûtent 3 $ une fois, créez un ensemble de boîtes de réception de test à l'avance et stockez les identifiants comme secrets CI. Cela évite d'avoir besoin d'appels API pour créer des boîtes de réception pendant le pipeline.
Une boîte de réception par suite de test, pas par test. À moins que vos tests envoient des e-mails conflictuels à la même adresse, une seule boîte de réception par suite fonctionne. Utilisez des sujets uniques ou des ID de messages pour distinguer entre les e-mails de test.
Nettoyez entre les exécutions. Avant chaque exécution de test, marquez tous les messages existants comme lus ou supprimez-les via IMAP. Cela empêche les e-mails obsolètes de causer de faux positifs.
# Nettoyez la boîte de réception avant l'exécution du test
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()
Stockez les identifiants de manière sécurisée. Traitez les identifiants de boîte de réception comme tout autre secret en CI — utilisez des variables d'environnement, pas des valeurs codées en dur.
Gérez les délais d'expiration avec élégance. La livraison d'e-mail peut prendre quelques secondes. Définissez votre délai d'expiration de sondage assez haut pour éviter les tests instables (30 secondes est une valeur par défaut raisonnable), mais pas tellement que un e-mail vraiment manquant accroche votre pipeline pendant des minutes.
Intégration du framework
La plupart des frameworks de test supportent les crochets de configuration/démontage qui rendent la gestion de la boîte de réception propre :
# exemple pytest
import pytest
@pytest.fixture(autouse=True)
def clean_test_inbox():
"""Marquez tous les e-mails comme lus avant chaque test."""
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
# Démontage : rien n'est nécessaire, les e-mails persistent pour la référence de la prochaine exécution
def test_welcome_email(app_client):
"""Vérifiez que l'inscription envoie un e-mail de bienvenue."""
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()
Analyse des coûts
| Scénario | Boîtes de réception | Coût |
|---|---|---|
| Petit projet, boîte de réception de test unique | 1 | 3 $ (une fois) |
| CI d'équipe avec suite-par-service | 5 | 15 $ (une fois) |
| Matrice de test complète (dev, staging, CI) | 10 | 30 $ (une fois) |
| Grande organisation, boîtes de réception par équipe | 50 | 150 $ (une fois) |
Comparez cela à un service de bac à sable à 15-35 $/mois (180-420 $/an) et l'économie est claire. Les boîtes de réception de test que vous créez aujourd'hui fonctionneront toujours dans un an, sans renouvellement.
Pour les équipes ayant besoin de création de boîte de réception programmatique (centaines de boîtes de réception, isolation par test), le niveau white-label à 30 $/mois inclut la création de boîte de réception illimitée via API.
Débogage des tests d'e-mail échoués
Quand un test d'e-mail échoue, la cause est généralement l'une de celles-ci :
L'e-mail n'est pas encore arrivé. Augmentez votre délai d'expiration de sondage. La livraison SMTP n'est pas instantanée — les messages peuvent prendre quelques secondes pour traverser le pipeline. Un délai d'expiration de 30 secondes est raisonnable pour la plupart des configurations.
Mauvaise boîte de réception. Vérifiez que votre application envoie à la même adresse que votre test sonde. Une erreur courante est de configurer l'application pour envoyer à une adresse différente de celle à laquelle votre code IMAP se connecte.
L'e-mail a déjà été lu. Si une exécution de test antérieure (ou un test antérieur dans la même exécution) a déjà marqué l'e-mail comme vu, votre recherche UNSEEN ne le trouvera pas. Utilisez le dispositif de nettoyage ci-dessus ou recherchez les messages par sujet et date plutôt que de vous fier uniquement au drapeau non vu.
L'authentification SMTP a échoué. Vérifiez que vos identifiants SMTP correspondent aux identifiants de boîte de réception gérée. Vérifiez que vous utilisez le port 587 avec STARTTLS, pas le port 465 avec SSL implicite.
Restrictions de pare-feu ou de réseau. Certains environnements CI restreignent les connexions sortantes. Assurez-vous que les ports 587 (SMTP) et 993 (IMAP) sont autorisés dans la configuration réseau de votre fournisseur CI.
Quoi ensuite
Cette approche vous donne des tests d'e-mail réels avec des protocoles standard. Pour plus sur les cas d'usage du développeur plus larges — isolation de staging, boîtes de réception par utilisateur, construction de produits d'e-mail — consultez le guide Email API for Developers.
Pour les détails sur la configuration de SMTP spécifiquement, y compris quand les serveurs SMTP faux ont plus de sens que la livraison réelle, lisez SMTP Testing: Test Outbound Emails Without Sending to Real Inboxes.
Et si vous évaluez le paysage des services d'e-mail jetables plus largement, ce guide couvre l'ensemble du spectre des boîtes de réception publiques gratuites aux comptes gérés.
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 →

