Test de composants automatisé avec Vitest et Testing Library

- Read in english
Ressources: huchet-vue

Les tests sont une partie essentielle du développement logiciel. Ils garantissent que nos composants fonctionnent comme prévu et nous aident à détecter les bogues tôt dans le processus de développement. Dans cet article, nous allons explorer comment tester automatiquement les composants Vue.js en utilisant Vitest et Testing Library.

Lorsque l'on travaille sur un projet Vite, Vitest est le meilleur choix pour tester nos composants. C'est un exécuteur de tests optimisé pour Vite et il est très facile à utiliser. Testing Library s'appuie sur Vue Test Utils et fournit un ensemble d'outils pour tester nos composants de manière centrée sur l'utilisateur.

Par exemple, Testing Library nous encourage à tester le comportement de nos composants plutôt que leur implémentation. C'est un concept puissant qui nous permet de tester les composants d'une manière plus proche de l'expérience utilisateur et facilite le refactoring.

Configuration de Vitest

Pour commencer, nous devons installer Vitest et Testing Library dans notre package packages/huchet-vue :

bash
pnpm add -D vitest @testing-library/vue jsdom

Étant donné que nous testons des composants frontend, nous avons besoin d'un environnement avec un DOM. Pour éviter de lancer un vrai navigateur, qui est lent et gourmand en ressources, nous pouvons utiliser JSDOM, une implémentation légère du DOM dans Node.js.

Pour faire fonctionner Vitest avec JSDOM, nous devons le configurer dans le fichier vite.config.ts :

ts
/// <reference types="vitest" />

import { defineConfig } from 'vite'

export default defineConfig({
  // ...

  test: {
    globals: true,
    environment: 'jsdom',
  },
})

Note

/// <reference types="vitest" /> est crucial pour garantir que les bons types soient disponibles dans le fichier de configuration de Vite. Cela augmente le fichier de configuration de Vite avec les types de Vitest.

Dans le fichier package.json de packages/huchet-vue, ajoutez deux nouveaux scripts pour exécuter les tests :

json
{
  "scripts": {
    "test": "vitest run",
    "test:watch": "vitest"
  }
}

Le premier script exécute les tests une fois, et le second script exécute les tests en mode surveillance s'il ne détecte pas un environnement CI.

Configuration TypeScript

Nos fichiers de test forment un nouveau contexte TypeScript. Nous devons accéder à l'API Node.js mais pas à l'environnement du navigateur. Comme pour tout nouveau contexte TypeScript, nous le configurons via un nouveau fichier TSConfig.

Créez un fichier tsconfig.test.json qui étend tsconfig.node.json et inclut les fichiers test.ts :

json
{
  "extends": "./tsconfig.node.json",
  "include": ["src/**/*.test.ts"]
}

Dans le fichier tsconfig.app.json, nous excluons les fichiers test.ts, de la même manière que nous avons exclu les fichiers story.vue :

json
{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": [
    "src/**/*.ts",
    "src/**/*.vue"
  ],
  "exclude": [
    "src/**/*.story.vue",
    "src/**/*.test.ts"
  ]
}

Dans notre fichier TSConfig principal, nous ajoutons une référence au fichier tsconfig.test.json :

json
{
  "files": [],
  "references": [
    {
      "path": "./tsconfig.app.json"
    },
    {
      "path": "./tsconfig.node.json"
    },
    {
      "path": "./tsconfig.story.json"
    },
    {
      "path": "./tsconfig.test.json"
    }
  ]
}

Écrire notre premier test

Chaque composant est isolé et encapsulé dans son dossier dès le départ. Les tests suivront la même structure. Créez un fichier src/Button/Button.test.ts et écrivez notre premier test :

bash
touch src/Button/Button.test.ts
ts
import { fireEvent, render, screen } from '@testing-library/vue'
import { describe, expect, it } from 'vitest'
import Button from './Button.vue'

describe('button', () => {
  it('doit émettre un événement de clic lorsqu\'il est cliqué', async () => {
    const { emitted } = render(Button, {
      props: {
        label: 'Hello',
      },
    })

    await fireEvent.click(screen.getByText('Hello'))

    expect(emitted()).toMatchInlineSnapshot(`
      {
        "click": [
          [
            MouseEvent {
              "isTrusted": false,
            },
          ],
        ],
      }
    `)
  })

  describe('solide', () => {
    it('doit rendre un bouton solide', () => {
      const button = render(Button, {
        props: {
          label: 'Hello',
          variant: 'solid',
        },
      })

      expect(button.html()).toMatchSnapshot()
    })
  })

  describe('contour', () => {
    it('doit rendre un bouton en contour', () => {
      const button = render(Button, {
        props: {
          label: 'Hello',
          variant: 'outline',
        },
      })

      expect(button.html()).toMatchSnapshot()
    })
  })
})

Dans le premier test, nous utilisons la fonction render de Testing Library pour rendre notre composant Button. Nous utilisons également la fonction fireEvent pour simuler un événement de clic sur le bouton. Enfin, nous utilisons la fonction expect de Vitest pour affirmer que le composant émet un événement click lorsqu'il est cliqué.

Dans les tests suivants, nous vérifions les variantes solid et outline du bouton. Nous utilisons la fonction toMatchSnapshot de Vitest pour affirmer que le composant se rend correctement.

Ces tests reflètent deux approches clés que je préfère lors des tests de composants :

  1. Tester le comportement du composant. Par exemple, émet-il les bons événements lorsqu'il est cliqué ?
  2. Tester l'apparence du composant. Par exemple, se rend-il correctement avec des variantes différentes ?

Dans la plupart des cas, ces deux types de tests suffisent à garantir que nos composants fonctionnent comme prévu, et ils peuvent être facilement réalisés à l'aide des tests par instantané.

Tip

Le test par instantané est un outil puissant qui nous permet de capturer la sortie de nos composants et de la comparer à un instantané de référence. Cela est très utile pour vérifier l'apparence de nos composants, garantissant qu'ils se rendent correctement et évitant d'écrire manuellement le HTML attendu.

Nous pouvons exécuter nos tests avec la commande suivante :

bash
pnpm run test

Tout fonctionne comme prévu !

bash
 DEV  v2.1.5 /huchet-vue/packages/huchet-vue

 src/Button/Button.test.ts (3)
 button (3)
 doit émettre un événement de clic lorsqu'il est cliqué
     ✓ solide (1)
       ✓ doit rendre un bouton solide
     ✓ contour (1)
       ✓ doit rendre un bouton en contour

 Fichiers de test  1 réussi (1)
      Tests  3 réussis (3)
   Début à  00:00:00
   Durée  536ms (transform 42ms, setup 0ms, collect 142ms, tests 22ms, environment 237ms, prepare 34ms)

Vérification de type des tests

En plus des tests, nous pouvons garantir que chaque type dans le projet est correct. C'est une manière simple de détecter les erreurs avant qu'elles ne se produisent, mais cela ne remplace pas les tests. Nous pouvons ajouter un nouveau script au fichier package.json pour exécuter la vérification de type :

json
{
  "scripts": {
    "typecheck": "vue-tsc -p tsconfig.app.json --noEmit"
  }
}

Ce script utilise la commande vue-tsc pour vérifier les types du projet dans le contexte du fichier tsconfig.app.json. L'option --noEmit empêche la transpilation des fichiers TypeScript en fichiers JavaScript, se concentrant uniquement sur la vérification des types sans construction.

Photo de profil d'Estéban

Merci de me lire ! Je m'appelle Estéban, et j'adore écrire sur le développement web.

Je code depuis plusieurs années maintenant, et j'apprends encore de nouvelles choses chaque jour. J'aime partager mes connaissances avec les autres, car j'aurais aimé avoir accès à des ressources aussi claires et complètes lorsque j'ai commencé à apprendre la programmation.

Si vous avez des questions ou souhaitez discuter, n'hésitez pas à commenter ci-dessous ou à me contacter sur Bluesky, X, et LinkedIn.

J'espère que vous avez apprécié cet article et appris quelque chose de nouveau. N'hésitez pas à le partager avec vos amis ou sur les réseaux sociaux, et laissez un commentaire ou une réaction ci-dessous—cela me ferait très plaisir ! Si vous souhaitez soutenir mon travail, vous pouvez me sponsoriser sur GitHub !

Continue readingIntégration continue et livraison simplifiée des composants