Introduction
Dans le développement d'applications front-end modernes, l'approche orientée composant est devenue un standard, offrant une modularité et une réutilisation accrues du code. Vue.js, en particulier, excelle dans ce domaine, permettant aux développeurs de construire des interfaces utilisateur complexes à partir de briques faiblement couplées. Cet article explore en profondeur les tests unitaires des composants Vue enfant, un aspect essentiel pour garantir la qualité et la fiabilité des applications Vue.
L'objectif est de fournir des bases solides pour implémenter le développement piloté par les tests (TDD) dans les projets Vue.js.
Pourquoi les Tests Unitaires sont Cruciaux pour les Composants Vue
Lorsqu’on choisit une technologie orientée composant pour son développement d’applications front end, on se donne la possibilité de construire son application à l’aide de briques faiblement couplées les unes aux autres. Cela constitue un avantage pour la réutilisation du code ainsi que pour les tests.
Avantages des Tests Unitaires
- Détection Précoce des Erreurs : Les tests unitaires permettent de détecter les erreurs dès le début du cycle de développement, réduisant ainsi les coûts et les efforts liés à la correction des bugs en phase de production.
- Amélioration de la Qualité du Code : En écrivant des tests unitaires, les développeurs sont encouragés à concevoir un code plus modulaire, plus lisible et plus maintenable.
- Documentation Vivante : Les tests unitaires servent de documentation vivante, illustrant le comportement attendu des composants et facilitant la compréhension du code par les autres membres de l'équipe.
- Confiance lors des Refactorisations : Les tests unitaires offrent une sécurité lors des refactorisations, permettant aux développeurs de modifier le code en toute confiance, sachant que les tests détecteront toute régression involontaire.
Mise en Place de l'Environnement de Test
Pour réaliser des tests unitaires efficaces, il est essentiel de mettre en place un environnement de test approprié. Voici les outils et les configurations nécessaires :
Outils de Test
- Jest : Un framework de test JavaScript tout-en-un, offrant un test runner, une bibliothèque d'assertions et un environnement de DOM simulé (jsdom). Jest est particulièrement adapté aux projets React, mais peut également être utilisé avec Vue.js.
- Enzyme : Une bibliothèque de test spécifique à React, offrant une API intuitive pour manipuler et interagir avec les composants React. Enzyme permet de réaliser du "shallow rendering", qui consiste à rendre uniquement le composant à tester, sans ses dépendances, ce qui facilite les tests unitaires.
- Vue Test Utils : La bibliothèque officielle de Vue.js pour les tests unitaires, offrant des outils pour monter, manipuler et interagir avec les composants Vue.
Configuration de Jest
Pour configurer Jest, il est nécessaire d'installer les dépendances appropriées :
Lire aussi: Grippe Enfant : Nouvelle solution de dépistage
babel-jest: Pour transpiler le code JavaScript moderne (ES6+) en code compatible avec Node.js.babel-preset-es2015: Un preset Babel pour transpiler le code ES6.babel-preset-react: Un preset Babel pour transpiler le code React (JSX).jest-cli: L'interface en ligne de commande de Jest.
La configuration de Jest peut être effectuée dans le fichier package.json, en spécifiant les options de test et les transformations à appliquer au code.
Configuration Globale
Afin de simplifier l’écriture des tests, il est recommandé de rendre globale la variable vm (pour Vue mounted : vm représente l'instance Vue après la création et avec le template chargé) et d’extraire un beforeEach à insérer avant les assertions. On pourra ainsi accéder à la “vue montée” vm, déjà construite dans chaque assertion.
let vmbeforeEach(function () { const Constructor = Vue.extend(Hello) vm = new Constructor().$mount()})Tester les Composants Vue Enfant
Les tests unitaires des composants Vue enfant consistent à vérifier que le composant se comporte comme prévu dans différentes situations.
Tester les Props
Les props permettent aux composants parents d’appeler les composants enfants en leur passant des propriétés. On souhaite pouvoir éditer le texte affiché par le bouton pour chaque utilisation du composant ClickMeButton. Ensuite, en ajoutant également le nom du composant parmi les enfants à exporter dans le template. Pour ce faire, c’est Hello.vue qui doit définir la propriété message à chaque utilisation d’un composant ClickMeButton.
Tester les Événements Émis
Le composant enfant expose une propriété EventEmitter avec laquelle il émet des événements lorsque quelque chose se passe. Une façon de faire est de brancher l’appel à la méthode incrementCounter à chaque fois que le composant ClickMeButton émet l’événement « buttonHasBeenClicked ».
Lire aussi: L'importance des tests auditifs
Tester les Méthodes
Il est primordial de réaliser le stub avant la construction de l’objet. Ensuite vient une nouvelle construction de la “vue montée” et l’appel de la méthode à tester qu’on appellera incrementFromTheDice.
Les assertions portent sur l’URL appelées par l’API. A la fin du test, il ne faut surtout pas oublier de restaurer l’état via la méthode Vue.http.get.restore.
Exemple de Test Unitaire Simple
Voici un exemple de test unitaire simple pour un composant Vue :
import { mount } from '@vue/test-utils'import MonComposant from './MonComposant.vue'describe('MonComposant.vue', () => { it('affiche le titre correctement', () => { const wrapper = mount(MonComposant) expect(wrapper.find('h1').text()).toBe('Mon titre') })})Dans cet exemple, le test vérifie si l'élément <h1> avec le texte "Mon titre" est présent dans le template du composant. Si l'élément est présent et a la bonne valeur, le test passera avec succès. Si l'élément n'est pas présent ou n'a pas la bonne valeur, le test échouera.
Explication du Code de Test
import { mount } from '@vue/test-utils': Importe la fonctionmountde la bibliothèque Vue Test Utils, qui permet de monter un composant Vue pour le tester.import MonComposant from './MonComposant.vue': Importe le composant Vue à tester.describe('MonComposant.vue', () => { ... }): Définit un bloc de test qui regroupe les tests pour le composantMonComposant.vue.it('affiche le titre correctement', () => { ... }): Définit un test individuel qui vérifie si le titre du composant est affiché correctement.const wrapper = mount(MonComposant): Monte le composantMonComposantet retourne un "wrapper" qui permet d'interagir avec le composant monté.expect(wrapper.find('h1').text()).toBe('Mon titre'): Vérifie si l'élément<h1>dans le wrapper a le texte "Mon titre".
Utilisation de configureTestingModule()
configureTestingModule est une méthode de la classe TestBed dans Angular, qui permet de configurer un module de test pour le composant à tester. Elle prend en entrée un objet qui décrit les dépendances du composant, notamment les déclarations de composants, les fournisseurs de services, les imports de modules, etc.
Lire aussi: Tout savoir sur la grippe chez les bébés
L'objet passé à configureTestingModule définit le contexte dans lequel le composant sera testé, en incluant les dépendances nécessaires pour que le composant fonctionne correctement. C'est donc un élément clé pour s'assurer que les tests unitaires sont fiables et reflètent le comportement réel du composant.
Dans ce cas précis, le code imports: [ MonComposantComponent ] indique que le composant MonComposantComponent est déclaré dans le module de test. Cela signifie que le composant sera disponible pour être utilisé dans le test unitaire.
Utilisation de compileComponents()
compileComponents() est une méthode de la classe TestBed qui compile les composants déclarés dans le module de test. Elle est utilisée pour garantir que les composants soient compilés avant le début des tests. Si compileComponents() n'est pas appelé, les tests pourraient échouer en raison de la non-compilation des composants.
L'utilisation de await avec compileComponents() est nécessaire car cette méthode retourne une promesse qui doit être résolue avant de continuer avec les tests. En utilisant await, on peut garantir que les composants seront compilés avant de commencer à tester le composant MonComposantComponent.
Utilisation de fixture.detectChanges()
fixture.detectChanges() est utilisé pour déclencher la détection des changements dans le composant, ce qui permet de mettre à jour les propriétés et les vues associées au composant. Cela est appelé avant chaque test pour garantir que le composant soit en état cohérent pour le test.
Si fixture.detectChanges() n'est pas appelé, les propriétés et les vues associées au composant ne seront pas mises à jour, ce qui peut entraîner des résultats incorrects pour les tests.
L'appel à fixture.detectChanges() après le clic sur le bouton est nécessaire pour que les modifications apportées au composant soient reflétées dans la vue. En effet, lorsque le bouton est cliqué, cela peut déclencher des mises à jour du modèle de données du composant, mais ces mises à jour ne seront pas reflétées dans la vue tant que fixture.detectChanges() n'a pas été appelé.
Il permet à Angular de détecter les modifications apportées au modèle de données et de mettre à jour la vue en conséquence.
Tests d'Intégration vs. Tests Unitaires
Il est important de distinguer les tests unitaires des tests d'intégration. Les tests unitaires se concentrent sur la vérification du comportement d'un composant individuel, tandis que les tests d'intégration vérifient l'interaction entre plusieurs composants ou modules.
L'idée d'Enzyme et plus largement des tests de composants Front est de faire du shallow rendering. Concrètement, prenons un composant C dans un composant B lui-même contenu dans un composant A. Chacun contient une div et titre. Cela rend le test vraiment unitaire d'un point de vue du composant car on se préoccupe uniquement de ce qui se passe dans A. Dans notre exemple, la conséquence est qu'on a pas besoin d'importer ou de mocker B.
shallow(): render du composant React selon le principe du shallow rendering.mount(): render du composant React en le montant réellement dans le DOM.
Le gros avantage c’est qu’Enzyme ne renvoie pas un DomElement comme les react-addons-test-utils mais un wrapper sur le composant rendu. C’est au travers du Wrapper que la manipulation de composant React est facilitée. L’API complète est décrite dans la documentation Enzyme. Autre avantage, on peut visualiser facilement dans la console, les composants rendu dans le test au travers de la méthode debug().
Alternatives aux Méthodes de Test Traditionnelles
Testing Library est une librairie fournissant des méthodes utilitaires pour requêter les éléments du DOM et simuler le comportement d’un utilisateur vis-à-vis d’un composant en prenant en compte la navigation à la souris et au clavier, ainsi que les spécifications d’accessibilité ARIA (Accessible Rich Internet Applications). Elle permet par ailleurs de visualiser les différents comportements dans une interface, ce qui peut s’avérer utile autant pour un développeur qu’un designer, et donc pertinent dans le cadre d’un Design System.
L’inconvénient principal - parce qu’il y en a toujours - c’est qu’il faille sortir l’artillerie lourde pour des composants qui ont majoritairement vocation à être légers et simples. En conséquence, comparée à une librairie spécifique aux tests unitaires de composants, elle souffre d’un manque de rapidité d’exécution.
Tests et Web Components
Les exemples de tests partagés testent des web components sans Shadow DOM. Cette spécificité qui fait partie intégrante du concept même du web component fait barrière lors de l’écriture des tests.
Par ailleurs dans le cas de Cypress, le sélecteur ne peut pas voir dans un shadow DOM sans passer par la méthode .shadow() qui permet d’entrer dans le composant.
Dans tous les cas, la question du Shadow DOM doit se poser non seulement pour vos tests, mais aussi parce que l'encapsulation de web components avec Shadow DOM pose des soucis d’accessibilité, notamment pour la lecture d’écran.
Bonnes Pratiques pour les Tests Unitaires des Composants Vue
- Écrire des Tests Simples et Concis : Chaque test doit se concentrer sur un aspect spécifique du comportement du composant.
- Utiliser des Noms de Tests Clairs et Significatifs : Les noms de tests doivent décrire clairement ce qui est testé.
- Éviter les Dépendances Externes : Les tests unitaires doivent être isolés et ne pas dépendre de facteurs externes, tels que les bases de données ou les API.
- Utiliser des Mocks et des Stubs : Pour isoler le composant à tester, il est souvent nécessaire d'utiliser des mocks et des stubs pour simuler les dépendances externes.
- Couvrir Tous les Cas d'Utilisation : Les tests unitaires doivent couvrir tous les cas d'utilisation possibles du composant, y compris les cas limites et les cas d'erreur.
- Maintenir les Tests à Jour : Les tests unitaires doivent être mis à jour chaque fois que le code du composant est modifié.
tags: #test #unitaire #composant #Vue #enfant
