AngularJS: Test unitaire avec angular-translate et $templateRequest

Au programme d’aujourd’hui, on parle de test unitaire avec angular-translate et $templateRequest. Pourquoi cet article ? Car j’ai rencontré la semaine dernière quelques soucis avec mes tests en utilisant ces deux services et j’en profite pour faire un article expliquant la démarche que j’ai entrepris pour les corriger.

On va commencer par se remettre dans le contexte. Je réalise une application avec AngularJS. Dans cette application, j’aurai besoin:

  • D’un système de traduction: je choisis angular-translate et je me repose sur des fichiers pour le stockage des traductions.
  • Un service me permettant de changer le contenu d’un layer global pour toute l’application. Je peux changer ce contenu directement via un élément ou bien depuis un path vers un fichier html.

Je réalise mon projet avec Yeoman et donc Grunt. En tant que bon développeur, je vais faire passer le tout sous des tests unitaires et pour se faire, j’utilise Karma/Jasmine. J’ai donc la structure et le code suivant.

Le projet est maintenant prêt ! Un petit git clone de celui-ci et on peut lancer les tests via un simple grunt test.

error_unit_test1
Noooooooooon ! Une erreur ?! Comment ?! Pourquoi ?! Pas de panique ! On va s’en occuper :). Commençons par comprendre l’erreur. D’après le message, il y a eu une requête inattendue sur le fichier de traduction. Pourquoi est-elle survenue ? Car on a lancé un rootScope.$digest(); dans le but d’effectuer le chargement du template. A coté, angular-translate s’est mis en route et a tenté de charger le fichier de traductions. Mais pourquoi cela produit-il une erreur ? La raison se trouve sur la documentation officielle d’angular-translate, où l’on nous explique que le fichier est chargé via une requête asynchrone (donc via XMLHttpRequest) et que dans les tests, il est nécessaire de spécifier tous les appels de ce type. On nous explique que l’on peut utiliser $httpBackend mais que dans leur exemple, cela ne fonctionne pas. Dans notre cas, on peut résoudre le problème de cette façon mais vu que par la suite, on nous propose une solution plus élégante et fonctionnelle dans tous les cas, c’est cette dernière que l’on va exploiter. On va donc implémenter un customLoader pour charger nos fichiers de traduction. Donc retour au code !
  1. Pour éviter les copier-coller dans les futurs tests où l’on risque d’avoir le même soucis, je vais centraliser le tout dans un fichier. Pour se faire, je vais créer un fichier asynchronousLoader.js dans un nouveau dossier nommé mock directement sous mon dossier test. Ce fichier permettra de regrouper tous les mocks ayant pour objectifs de palier au soucis résultant de chargement asynchrones de fichiers. On a donc ceci: test/mock/asynchronousLoader.js.
  2. Dedans, on va réaliser une simple fonction qui contient un bête et méchant copier-coller de la fonction présente dans la documentation. Le code est disponible ici.
  3. Ensuite, étant donné que la configuration de karma est déjà réglée pour inclure les fichiers contenus dans le dossier mock, il ne nous reste plus qu’à utiliser cette fonction dans notre test.

Maintenant que tout est bon, on relance le test !

error_unit_test2
O_o. Monde cruel ? Encore une erreur ? Et oui mais c’est pas si mal ! Pourquoi me direz-vous. Tout simplement car l’erreur a changé ! On a donc résolu notre premier soucis. Maintenant, on peut passer au suivant qui ne se révélera pas vraiment plus compliqué. Déjà, recommençons par le début: comprendre l’erreur ! À la lecture du message d’erreur, on se rend rapidement compte que l’on est face au même problème mais avec un fichier différent. Cette fois, c’est le service $templateRequest qui nous gène. On l’exploite dans notre service dans le but d’aller chercher le contenu du layer depuis un fichier. Maintenant que l’on comprend le problème, il ne nous reste qu’à le résoudre. On doit donc déclarer l’appel à un fichier de template en asynchrone. C’est partit !
  1. Une fois encore, je vais centraliser le code dans une méthode que je vais placer dans test/mock/asynchronousLoader.js. Ainsi je pourrai économiser le copier-coller. On y rajoute donc la méthode mockViewsLoader. Cette méthode se chargera de prévenir tous les appels possibles à des fichiers finissant en .html et qui seraient présent dans le dossier views ou un dossier descendant. Elle offre également la possibilité de passer en premier paramètre un contenu qui sera celui renvoyé par le chargement du fichier. Si rien n’est fournit, on utilisera un simple objet String.
  2. Ensuite, il suffit d’implémenter la fonction dans notre test et de mettre à jour celui-ci pour changer la valeur espérée. On va également flush $httpBackend dans le but que la promise de $templateRequest soit résolue et que notre test soit exécuté.

On peut relancer le test et là, oh miracle, tout fonctionne !

valid_unit_test

Voilà qui termine mon article. Vous pouvez retrouver l’intégralité du code sur Github:

  1. Trois branches sont présentes:
    1. La branche master représente le projet dans son état initial.
    2. La branche v2/fix-problem-load-translation-file représente le projet une fois le soucis de chargement du fichier de traduction résolu.
    3. La branche v3/fix-problem-load-template représente le projet une fois le soucis de chargement du fichier html résolu et où tous les tests passent.
  2. Vous y trouverez également 2 pull-requests. La première présente la résolution du premier problème et la seconde la résolution du second et dernier problème.

N’hésitez pas à laisser un commentaire (ici ou sur Github) si quelque chose vous chiffonne ou bien si vous avez un soucis ;).

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *