CodeIgniter 4 : Ecrire un test unitaire pour l'envoi d'emails

Un test unitaire pour tester que les emails peuvent être envoyés dans notre application

Pour faire court : je ne suis pas très habitué aux tests unitaires, et je voulais apprendre quelque chose de nouveau. J'ai décidé d'écrire un test unitaire pour m'assurer que je peux envoyer des emails depuis mon application CodeIgniter 4 .

Eh bien, il s'avère que par défaut, le service d'email de CodeIgniter 4 utilise une Mocked Class pour le faire, et donc mon test retournait toujours true pour la méthode send() de la classe Email.

Rédaction du test

Pour commencer, voici le test que je voulais effectuer :

$email = service('email') ; $email->setFrom(env('email.from'), env('email.fromName')) ; $email->setTo(env('email.testRecipient')) ; $email->setSubject('Email Test') ; $email->setMessage('Tester que l'email peut être envoyé correctement.') ; $this->assertTrue($email->send()) ; // Pourquoi cela renvoie-t-il toujours true ?

En général, l'idée semble bonne. Mais il y avait un problème. Mon test renvoyait toujours true, même si l'email n'était pas envoyé.

Pourquoi le test renvoie-t-il true ?

Je ne suis pas encore un expert en tests unitaires, et j'ai donc pensé que je faisais quelque chose de mal. Bien sûr, je m'attendais à ce que le test soit réussi, mais le courriel n'était pas envoyé. De plus, je n'ai pas encore d'expérience avec CodeIgniter 4. J'ai donc décidé de rechercher le problème, et j'ai trouvé le "coupable", pour ainsi dire.

Par défaut, CodeIgniter effectue des tests en utilisant des classes fictives prédéfinies, c'est-à-dire un ensemble de fausses classes qui ne doivent pas effectuer d'actions définitives.

Dans la documentation, il est dit que c'est fait exprès pour empêcher un comportement de test intrusif (voir ici : Testing).

Mon test retournait toujours vrai parce qu'il utilisait la méthode send() de la classe fantaisie, et non la vraie classe Email.

La classe MockEmail

En effet, la classe MockEmail se présente comme suit :

class MockEmail extends Email { /** * Valeur à renvoyer à partir de la méthode simulée send().
     * @var bool */ public $returnValue = true ; public function send($autoClear = true) { if ($this->returnValue) { $this->setArchiveValues() ; if ($autoClear) { $this->clear() ; } Events::trigger('email', $this->archive) ; } return $this->returnValue ; } }

Comme vous pouvez le voir, la méthode send() n'envoie pas réellement l'e-mail. Ou, du moins, j'avais besoin d'envoyer un message SMTP et cette méthode ne le fait pas.

Vous pourriez probablement surcharger la classe MockEmail, mais cela ne m'a pas semblé être un scénario réel. Je veux dire que je veux tester que l'application peut réellement envoyer des courriels à mes utilisateurs, donc je dois suivre le même chemin, sinon le test n'aurait pas beaucoup de sens, n'est-ce pas ?

Si je dois tester que je peux recevoir l'email, je ne veux pas d'un mock send(), n'est-ce pas ?

Surcharger les méthodes setUpMethods de la classe de test

Dans la documentation, il est également indiqué que vous pouvez supprimer 'mockEmail' de la classe Test, dans les $setUpMethods.

Dans le code de $setUpMethods, il y a ce commentaire d'avertissement:

// CodeIgniter\Test\CIUnitTestCase.php /** * Méthodes à exécuter pendant le setUp * * WARNING : Do not override unless you know exactly what you are doing * * This property may be deprecated in the future * * @var array of methods */ protected $setUpMethods = [ 'resetFactories', 'mockCache', 'mockEmail', 'mockSession', ] ;

Cette solution semble donc déconseillée. Mais qu'est-ce qu'un "comportement intrusif" dans la phase de test ? Je n'en suis pas certain. Peut-être que si vous rendez les tests disponibles avec des appels publics (get, post), il pourrait y avoir un problème. Ce n'est pas mon cas.

Solution et test fonctionnel

Ma solution est de contourner la classe simulée, comme suit.

<?php // tests\\Emails\SendEmailTest.php namespace App\Emails ; use CodeIgniter\Test\CIUnitTestCase ; final class SendEmailTest extends CIUnitTestCase { private $email = null ; public function __construct() { parent: :__construct() ; // Suppression du mockEmail de ce test $k = array_keys($this->setUpMethods, 'mockEmail')[0] ; unset($this->setUpMethods[$k]) ; } public static function setUpBeforeClass() : void { parent::setUpBeforeClass() ; return ; } protected function setUp() : void { parent::setUp() ; $this->email = service('email') ; $this->email->setFrom(env('email.from'), env('email.fromName')) ; $this->email->setTo(env('email.testRecipient')) ; return ; } final public function testCanSendEmails() { $this->email->setSubject('Email Test') ; $this->email->setMessage('Tester que l'email peut être envoyé correctement.') ; $result = $this->email->send() ; if (!$result) { d($this->email->printDebugger()) ; } $this->assertTrue($result) ; } }

Conclusions

La question est la suivante : ai-je raison de penser que ce comportement n'est pas logique et qu'il devrait être amélioré, ou existe-t-il une bonne raison de le faire, que je ne connais pas ? Existe-t-il un meilleur moyen d'y parvenir ?

Faites-moi part de votre opinion dans les commentaires ci-dessous ! Si vous avez aimé cet article, suivez-moi sur Facebook et Youtube!

Laisser un commentaire

Tous les commentaires seront soumis à approbation après avoir été envoyés. Ils pourront être publiés après quelques heures.

Vous pouvez simplement utiliser un pseudo au hasard, cela me permet au moins de répondre à vos commentaires. Et si vous choisissez de soumettre votre email, vous pouvez recevoir une notification à chaque fois que je réponds à votre commentaire.

Aucun commentaire n'a été rédigé jusqu'à présent sur cet article. Soyez le premier à partager votre opinion !

*