Jasmine partie 2 : Les mocks

Publié par Jean-Nicolas Viens le mardi 23 juillet 2013 à 14:26

Pour faire suite à l’article précédent à propos de Jasmine, voici un nouvel aspect de cet outil : les mocks. Ceux-ci nous permettent de tester un composant de notre code en isolation. On peut ainsi valider l’interaction avec un autre composant ou forcer une réaction de la part de ce composant. Tout cela avec la syntaxe fluente de Jasmine :

spyOn(conversion, "livreEnKilo").andReturn(10);

Utilisation des mocks

Vocabulaire : Jasmine offre la méthode spyOn(); et utilise le terme “espion” pour les objets ainsi crées. Cependant, ces objets sont selon moi des mocks en réalité. J’utiliserai donc le terme mock dans cet article. Martin Fowler a un article sur les différences entre les stubs et les mocks qui est très complet. Il y a aussi cette réponse sur stackoverflow qui résume bien les différences.

Utilisant la flexibilité d’un langage dynamiquement typé tel le Javascript, Jasmine nous propose une façon simple et élégante de créer des mocks. Voici un exemple :

spyOn(objet, "méthode").comportement()

On utilise la méthode spyOn en lui passant l’instance de l’objet à “mocker” ainsi que le nom dela méthode sous forme de string. On peut également associer un comportement à notre mock avec une de ces méthodes :

  • par défaut : la méthode est sans comportement et retourne toujours undefined.
  • andCallThrough() : Installer le mock, mais utiliser la méthode originale lors de l’appel
  • andReturn(<retour>) : Retourner cette valeur à l’appel de la méthode
  • andCallFake(<factice>) : appeler la méthode sur un second objet factice

Si vous voulez “mocker” une méthode globale (ce qui devrait être rare), rappelez-vous que tout objet global est en fait à l’intérieur de la variable window. Vous pouvez donc utiliser, par exemple, spyOn(window, "alert”); .

Maintenant, comment valider qu’il y a eu une interaction avec un mock? Avec la méthode expect(), comme pour les assertions! Il suffit de passer la fonction “mockée” en paramètre et utiliser soit toHaveBeenCalled() ou toHaveBeenCalledWith(<paramètres>). Comment cela fonctionne-t-il? Il faut simplement se rappeler que objet.action() appelle la fonction action() sur objet et retourne le résultat de celle-ci, alors que objet.action retourne la fonction action elle-même sans l’exécuter. Jasmine tire donc profit de cette fonctionnalité du Javascript pour nous permettre de faire :

spyOn(window, "alert");
alert("test");
expect(window.alert).toHaveBeenCalledWith("test");

Note : toHaveBeenCalledWith() valide également le type du paramètre, donc “10” (string) et 10 (integer) ne sont pas pareils ici.

Attention, il est fréquent de voir expect(window.alert()), ce qui ne fonctionne pas puisque le retour de la fonction alert n’est pas un mock!

On peut également valider seulement le type du paramètre :

spyOn(window, "alert");
alert("test");
expect(window.alert).toHaveBeenCalledWith(jasmine.any(String));

Note : Tout type ou constructeur est valide ici (Function, String et Number sont sûrement les plus utilisés)

Nous pouvons donc valider que le paramètre est exactement la valeur voulue ou du bon type, mais il est impossible de valider une condition sur ce paramètre. En fait, Jasmine n’offre pas cette fonctionnalité “out of the box”, mais l’outil est assez bien construit pour que ce soit trivial d’implémenter ce comportement. J’ai donc écrit ce plugin qui permet de valider un paramètre de la façon suivante :

expect(window.alert).toHaveBeenCalledWith(jasmine.Arg().contains("est"));

Nous ne nous y attarderons pas dans cet article, mais notez qu’il est également possible de créer un mock à la volée avec createSpy() et createSpyObj().

Pour terminer, voici une démonstration sur les mocks.

Cet exemple utilise également la méthode beforeEach(), dont l’utilité devrait être claire à ceux habitués aux outils de la famille xUnit. Cette méthode sera exécutée une fois avant chaque spécification. Notez que les beforeEach() sont cumulatifs lorsque l’on dispose de spécifications imbriquées. Bien entendu, il existe également afterEach().

Une astuce intéressante est de remarquer que tous les appels AJAX dans jQuery passent par la méthode $.ajax(), ce qui rend les choses faciles à tester. En voici un exemple :

Conclusion

En terminant, sachez que vous pouvez également utiliser Sinon.js, qui est un framework de mocks en Javascript. Celui-ci cohabite bien avec Jasmine, si vous avez une préférence pour Sinon.js.

Dans le troisième et dernier billet à propos de Jasmine, nous utiliserons les mocks dans un but étrange, mais puissant : contrôler le temps!

Lire le billet suivant

blog comments powered by Disqus

0 Comments:

Post a comment

Comments have been closed for this post.