Capturer les e-mails dans les tests, c’est cesser de traiter le courrier comme quelque chose qui quitte la machine. Au lieu de laisser un build envoyer via un fournisseur public puis interroger une boîte dans le monde extérieur, l’application est pointée vers un point de réception sur l’hôte de build, et le test lit ce qui a été remis. Le résultat est plus rapide, fonctionne hors ligne et échappe aux incidents du fournisseur qui, sinon, transforment une suite verte en suite rouge.
Pourquoi un pipeline ne devrait pas dépendre d’un fournisseur de messagerie
Un test qui envoie du vrai courrier via un vrai service a importé tout ce que ce service a de fragile dans son propre résultat. L’authentification peut expirer, les quotas peuvent être épuisés, le fournisseur peut être brièvement indisponible, et aucune de ces issues ne dit quoi que ce soit sur votre code. Pire, elles sont indiscernables des échecs que vous voulez réellement attraper, si bien que l’équipe apprend à relancer le pipeline au lieu de le lire.
Il y a un second coût facile à négliger : un pipeline qui envoie du vrai courrier doit qu’on lui dise où l’envoyer. Si cette destination est une vraie adresse, chaque exécution livre du trafic de test à quelqu’un. Pointer le build vers un point local supprime entièrement la question, car rien ne quitte le réseau.
Comment fonctionne un point de capture local ?
Le mécanisme est le même que celui utilisé par n’importe quel serveur de messagerie. Votre application est configurée avec un hôte et un port de serveur de messagerie pendant toute la durée de l’exécution du test. Cet hôte est l’interface de bouclage et le port est celui que le harnais a choisi pour cette exécution, si bien qu’aucune valeur de configuration ne doit être affirmée comme un fait sur le monde.
Une fois que l’application remet le message, le point de capture l’accepte, le garde en mémoire ou l’écrit dans un fichier, et le rend disponible au test. Rien n’est relayé nulle part.
Cette forme apporte trois propriétés qui rendent les assertions fiables :
- Le message existe avant que le test ne le cherche, car la remise est synchrone, donc il n’y a pas de boucle d’interrogation à rater.
- Le contenu est exact, en-têtes et encodage compris, car rien ne l’a réécrit en transit.
- Le message peut être relu autant de fois que le test en a besoin, car il est stocké plutôt que consommé.
Que contient réellement un message capturé ?
Plus que le corps, et les parties supplémentaires sont là où vivent les assertions utiles. Un message capturé porte les informations d’enveloppe issues de la remise en même temps que le message lui-même, ce qui signifie qu’un test peut vérifier à qui le message prétend être de la part, à quelle adresse il a été envoyé, l’objet, le type de contenu, et le corps sous la forme que votre application a produite.
Cela compte parce que la livraison n’est pas ce qui est testé. Le contenu l’est. Une suite qui vérifie seulement qu’un message a été accepté passe joyeusement pendant que le gabarit affiche un nom vide ou que le lien pointe vers le mauvais environnement.
Un test en échec devrait-il conserver le message brut ?
Oui, et il devrait le conserver délibérément plutôt que par accident. Quand une assertion sur le corps échoue, l’artefact le plus utile est le message tel qu’il a réellement été produit. Sans lui, l’étape suivante consiste généralement à reproduire l’échec localement à la main, ce qui est précisément le travail manuel que le point de capture était censé supprimer.
Deux habitudes rendent l’artefact utile. Attachez-le à l’exécution en échec plutôt qu’à un emplacement partagé, afin que des exécutions concurrentes ne puissent pas s’écraser mutuellement. Et traitez-le comme des données de test lorsqu’il est stocké : un message capturé peut contenir des adresses et des valeurs générées pour l’exécution, et il devrait être conservé selon le même calendrier court que le reste de la sortie de l’exécution. Les adresses utilisées ainsi existent pour les assertions, jamais comme de vraies identités.
Quand vous avez besoin d’une vraie boîte à la place
Une capture locale ne peut pas répondre aux questions qui impliquent le monde extérieur. Si le test doit prouver qu’un message survit à un vrai chemin de livraison, ou qu’un système tiers y réagit, alors quelque chose doit quitter la machine.
Pour ces cas, une boîte jetable suffit souvent : une adresse créée pour l’exécution, lue une fois, jetée. La page de courrier temporaire en crée une sur demande, et comme personne ne l’a enregistrée, la boîte démarre vide et ne contient que le trafic déclenché par le test. La distinction vaut la peine d’être gardée claire dans votre esprit : la capture locale sert aux assertions sur ce que votre application envoie, et une vraie boîte sert aux assertions sur ce qui arrive.
Pour les développeurs : ports, parallélisme et assertions
Quatre décisions déterminent si cet arrangement reste ennuyeux.
Lier le point de capture à la seule interface de bouclage est la première. Cela limite l’exposition et rend évident que le point n’est pas un service de messagerie pour quoi que ce soit en dehors de l’exécution. Laissez le port venir de l’environnement plutôt que d’une constante, afin que deux exécutions sur une même machine ne puissent pas entrer en collision.
Isoler les exécutions est la deuxième. Donnez à chaque exécution son propre processus de point de capture, ou au moins son propre stockage, et donnez à chaque cas sa propre adresse de destinataire. Une file partagée lue par plusieurs tests en parallèle produit la classe d’échecs la plus irritante qui soit : un test qui passe seul et échoue dans un pipeline complet.
Affirmer le contenu plutôt que le temps écoulé est la troisième. Comme la remise est synchrone, il n’y a rien à attendre ; une pause dans ce genre de test est le signe que quelque chose est testé à travers la mauvaise interface.
Échouer bruyamment est la quatrième. Quand une assertion sur un message échoue, imprimez ou attachez la partie qui a été comparée. Un test qui ne rapporte qu’une non-concordance, sans montrer le message qu’il a lu, renvoie la personne suivante à la reproduction manuelle.
Si votre chemin de capture alimente des tests qui lisent des codes courts plutôt que des corps, les tests d’OTP dans les suites de bout en bout couvrent le côté analyse, et l’approche de la boîte fourre-tout couvre ce qu’il faut faire quand un environnement a réellement besoin d’un domaine entier plutôt que d’un seul point.
Étapes suivantes
Trouvez le test de votre suite qui envoie du courrier via un fournisseur, et comptez combien de fois il a échoué pour des raisons sans rapport avec le changement testé. Donnez-lui ensuite un point local pour une seule exécution et comparez. Gardez le fournisseur public pour ce qui a réellement besoin de l’Internet ouvert ; le reste appartient à la machine qui exécute les tests.