get()
Implémentation basique Mock d’un appel d’API Déclaration du mock Instanciation du mock Ajout du provider Amélioration du test unitaire Correction de l’implémentation ConclusionTester unitairement un service Angular
Les tests unitaires représentent une part importante du développement.
Si vous êtes adeptes du TDD (Test Driven Development), je ne vous apprendrai pas leur utilité.
Dans cet article, je vais vous présenter comment tester un service Angular.
En particulier, si votre service nécessite l’injection d’un autre service, vous verrez comment réaliser ce qu’on appelle un mock.
Prérequis
Si vous souhaitez réaliser les exercices au fur et à mesure de la lecture de cet article, vous devez disposer des outils suivants installés sur votre poste :
Outil | Version |
---|---|
NodeJS | 10.19.0 |
NPM | 6.14.3 |
Angular | 9.1.0 |
Si ce n’est pas le cas, vous pouvez vous référer à la documentation officielle de chacun.
Le code source complet des exemples ci-dessous est disponible sur GitHub : https://github.com/benjaminprevot/2020-04-03-tester-unitairement-un-service-angular
Rappels
Test unitaire
Pour simplifier, les tests unitaires permettent de valider une partie précise de votre programme.
En particulier, ils évitent les régressions lors du cycle de développement.
Intégrés dans une chaîne d’intégration continue, ils représentent une étape importante de la validation du code source.
Mock
Les mocks sont des objets dits “simulés”.
Ils permettent de gérer le comportement d’une instance en forçant sont comportement.
Lors de la mise en place de tests unitaires, ils sont très utiles pour définir un comportement immuable dans le temps d’objets qui sont utilisés au sein de votre code.
Cela évite des comportements imprévisibles qui fausseraient les résultats des tests.
Création de l’application
Passons maintenant à la pratique.
Tout d’abord, nous allons créer une nouvelle application Angular pour vous guider dans la mise en place des tests unitaires.
Pour cela, vous devez ouvrir un terminal de commande et vous positionnez dans le répertoire de travail.
Dans mon cas, je me place dans le répetoire workspace
de mon répertoire personnel, sous Linux.
Ensuite, la création de l’application mon-application
se fait via la commande ci-dessous :
Quelques questions vous serons posées, leurs réponses n’ont pas d’importance ici.
Lorsque l’exécution est terminée, il faut maintenant se placer dans la répertoire de l’application (c’est dans ce répertoire que les commandes suivantes seront lancées).
Notre appliation est maintenant créée et prête à être testée.
Création du service
Nous allons maintenant créer le service et le test unitaire correspondant.
Pour cela, Angular met à disposition une commande permettant de créer le fichier contenant la définition du service et le fichier pour le test unitaire correspondant.
Je ne rentrerai pas dans le détail de cette commande, vous pouvez vous référer à la documentation officielle pour plus de détails : https://angular.io/cli/generate#service
Dans le répertoire src/app
, nous disposons de 2 fichiers supplémentaires :
mon-service.ts
: définition de notre servicemon-service.spec.tx
: test unitaire de notre service
Par défaut, le test unitaire contient le code pour vérifier que le service est correctement créé.
Exécution des tests unitaires
Pour lancer les tests unitaires, il faut utiliser la commande suivante :
Vous verrez alors les différentes étapes de compilation et l’exécution des tests.
Une fenêtre de votre navigateur s’ouvre avec un rapport d’exécution.
En particulier, la liste des tests unitaires et leur état est disponible.
Les autres informations de ce rapport ne nous intéressent pas ici.
Comme vous avez pu le remarquer, la console est “bloquée”.
En effet, la commande précédente lance un processus permettant de rafraichir le rapport au fur et à mesure que le code est modifié.
Nous pouvons maintenant faire évoluer le service et compléter les tests.
Ajout d’un nouveau test unitaire
Afin respecter le principe du TDD, nous allons ajouter un test vérifier le résultat de notre service.
Pour cela, nous allons vérifier que l’appel à la fonction get
de notre service retourne la valeur Hello World!
.
Il faut alors éditer le fichier src/app/mon-service.spec.tx
.
Nous rajoutons le bloc suivant :
Je ne rentrai pas dans le détail de la syntaxe.
Petites précisions sur ce test :
- Le résultat de la fonction
get
est en fait de typePromise<string>
. C’est pourquoi, il faut utiliserthen()
afin de s’abonner au résultat. - L’exécution est asynchrone, il faut mettre en place le paramètre
done
et notifier de la fin de l’exécution du test viadone()
.
Si vous enregistrer cette modification, le rapport des tests n’est pas mis à jour puisqu’il y a une erreur dans la console.
En effet, nous appelons la fonction get()
dans le test unitaire alors qu’elle n’est pas définie dans le service.
Nous allons maintenant la mettre en place pour pouvoir faire fonctionner le test.
Création de la fonction get()
Ouvrez maintenant le fichier src/app/mon-service.ts
.
Pour définir la fonction get()
, il suffit de rajouter le code ci-dessous :
Cette fois, notre code compile et le rapport des tests est mis à jour.
Nous obtenons une erreur indiquant que notre résultat est null
.
C’est bien ce que nous avons écrit.
Implémentation basique
Afin de faire fonctionner notre service, nous allons remplacer la déclaration de la fonction.
Pour l’instant, nous allons utiliser retourner Hello World!
à chaque appel de la fonction.
Lorsqu’on enregistre les modifications, il n’y a plus d’erreur dans la console et le rapport est à jour indiquant que tous les tests ont réussi.
Nous allons améliorer un peu notre service afin qu’il appelle une API et retourne le résultat.
Mock d’un appel d’API
Comme précédemment, nous allons d’abort améliorer notre test unitaire.
Notre service va appeler une API, mais nous n’allons pas réellement réaliser l’appel HTTP.
En effet, il faudrait mettre un place un serveur web dédié au test et il s’agirait plus d’un test d’intégration que d’un test unitaire.
Notre but ici est de vérifier que le résultat de l’API est bien retourné par notre service.
Nous allons donc mettre un place un mock de l’API afin de simuler son comportement.
L’appel de l’API sera fait via HttpClient
.
Déclaration du mock
Tout d’abord, nous allons déclarer le mock au début du bloc describe
.
N’oubliez pas d’ajouter l’import de HttpClient
.
Instanciation du mock
Nous ajoutons maintenant un block beforeEach
pour instancier le mock.
On indique ici que nous allons réaliser un mock de type HttpClient
pour la fonction get
.
Il est possible d’instancier le mock directement au moment de la déclaration.
J’ai une préférence pour le faire dans un bloc beforeEach
afin de réinitialiser le mock à chaque test.
Ajout du provider
Il faut maintenant indiquer que le mock doit être utilisé lors de l’injection.
Pour cela, il faut modifier l’appel à la méthode configureTestingModule
.
Ainsi, à chaque injection de HttpClient
, c’est notre mock qui sera pris en compte.
Amélioration du test unitaire
Nous modifions maintenant notre test afin de :
- Simuler le comportement de l’appel à l’API
- Vérifier que le résultat de cet appel est retourné par notre service
On remplace le test précédent par ce code :
Il faut également ajouter l’import
La ligne concernant le mock simule l’appel à la fonction get
lorsqu’elle est appelée avec le paramètre /api
pour qu’elle retourne Résultat API
.
En sauvegardant, on obtient une erreur indiquant que le résultat n’est pas celui attendu.
En effet, nous n’avons pas changé l’implémentation de notre service.
Il retourne toujours Hello World!
alors que nous attendons Résultat API
.
Correction de l’implémentation
Nous allons maintenant utiliser HttpClient
dans notre service.
Dans notre service, nous ajoutons l’injection de HttpClient
en modifiant le constructeur :
N’oubliez par l’import
Enfin, il faut remplacer la fonction get()
comme ci-dessous :
On précise ici que nous appelons l’API /api
et que nous retournons son résultat sous forme de Promise
.
En sauvegardant, on obtient le rapport suivant :
Conclusion
Nous avons vu ici comment tester un service Angular et réaliser un mock.
Pour HttpClient
, Angular met à disposition un module de testing intégré. Le but ici est de donner une façon de faire qui peut être appliquée quelque soit le type souhaité.
Je vous mets à disposition les sources complètes sur GitHub : https://github.com/benjaminprevot/2020-04-03-tester-unitairement-un-service-angular