L'environnement de test

Comme indiqué dans la section Qu'est-ce qu'un test ?, les tests en JavaScript ne sont fondamentalement que du code dont nous confirmons qu'il s'exécute correctement, c'est-à-dire sans générer de Error. Cependant, cette définition est simpliste qu'elle ne prend pas en compte l'emplacement d'exécution du code, c'est-à-dire son environnement de test.

De manière générale, l'environnement de test peut être considéré comme deux composants: l'environnement d'exécution que vous utilisez pour exécuter le test (tel que Node ou le navigateur) et les API à votre disposition.

Environnement d'exécution

Les environnements d'exécution tels que Node, ou des outils similaires comme Deno ou Bun, sont conçus pour prendre en charge le code JS côté serveur ou à usage général. Leurs environnements n'incluent pas les API auxquelles vous pourriez vous attendre dans un navigateur, telles que la création et l'utilisation des éléments DOM et HTML, ni aucun concept de composant visuel ou de cible de rendu (c'est-à-dire pas seulement des éléments, mais d'afficher visuellement ces éléments avec CSS dans une fenêtre d'affichage).

Par conséquent, ces environnements d'exécution à usage général échoueront si vous essayez, par exemple, d'afficher des éléments React afin qu'ils puissent être testés, car il n'existe aucun objet document ni window disponible.

En revanche, si vous exécutez vos tests dans un navigateur, les API intégrées auxquelles vous pouvez vous attendre de ces environnements d'exécution risquent de ne pas être disponibles sans polyfill ou travail supplémentaire. Un piège courant consiste à lire et à écrire des fichiers : il n'est tout simplement pas possible de import { fs } from 'node:'fs'; dans un navigateur et de lire un fichier de cette manière dans le cadre d'un test.

Ce problème d'API "Web" par rapport à une API "backend" dépasse légèrement le cadre d'un simple test, car il peut être gênant d'avoir un codebase à la fois avec des parties serveur et client, mais cela est lié à l'idée d'écrire du code testable, que nous reviendrons tout au long de ce cours.

Tester des algorithmes ou la logique métier

Le fonctionnement d'une partie de votre code ne nécessite pas d'importations de nœuds ou de navigateurs, et donc des tests. Nous y reviendrons plus tard dans ce cours, mais la structuration de votre codebase de sorte que sa "logique métier" pure soit distincte du rendu ou du code spécifique aux nœuds peut faciliter les tests.

Prenons un exemple rapide : vous pouvez avoir une fonction Node qui lit et écrit un fichier à partir du disque, en le modifiant au cours du processus. En refactorisant votre fonction pour qu'elle accepte les fonctions qui effectuent la lecture et l'écriture à partir du disque, vous pouvez la tester n'importe où.

Dans ce cas, vous pouvez utiliser n'importe quel environnement pour tester ce code, dans un environnement d'exécution côté serveur ou dans le navigateur. Dans votre test, vous pouvez fournir des assistants qui stockent un fichier virtuel en mémoire ou renvoient des données d'espace réservé. Ce type d'outil d'aide peut être introduit dans un test, car il n'est pas important de vérifier, par exemple, que fs.writeFileSync fonctionne. Concentrez-vous sur votre code et ce qui le rend unique ou risqué.

Émuler les API de navigateur

De nombreux frameworks de test, tels que Vitest, vous offrent la possibilité d'émuler l'environnement d'API du navigateur sans exécuter de navigateur. Vitest utilise une bibliothèque appelée JSDOM en interne. Cela peut être un bon choix pour les tests de composants simples où les frais généraux liés à l'utilisation d'un navigateur sont élevés.

Les bibliothèques d'émulation présentent une caractéristique commune : bien qu'elles puissent émuler un navigateur (par exemple, le DOM, les éléments et les cookies), elles ne comportent pas de composant visuel. Cela signifie qu'elles permettront de travailler de manière impérative avec les éléments HTML et d'autres primitives, mais que vous ne pouvez pas afficher la sortie dans une image ou un écran, ni vérifier la position d'un élément en pixels sur la page.

Là encore, ce choix peut convenir aux tests de composants, où un composant représente un élément React, un composant Web, etc. Ces types de composants créent et interagissent généralement avec le DOM de manière relativement petite. Un navigateur émulé peut fournir suffisamment de fonctionnalités pour confirmer que le composant fonctionne comme vous le souhaitez. Une section à venir comprend un exemple de test de composant React avec Vitest et JSDOM.

L'émulation d'un navigateur est une pratique bien établie (JSDOM a été publié en 2014), mais elle sera toujours différente de l'utilisation d'un vrai navigateur. Ces différences peuvent être évidentes: par exemple, JSDOM n'inclut pas de moteur de mise en page. Il n'y a donc aucun moyen de vérifier la taille d'un élément ni de tester un geste complexe tel qu'un balayage. Les différences peuvent également être subtiles et inconnues. C'est pourquoi il est préférable de limiter la concision de vos tests basés sur JSDOM afin de pouvoir "timeboxer" le risque que tout comportement dévie de la réalité.

Contrôlez un vrai navigateur

Pour tester votre code tel que vos utilisateurs le verront, le meilleur choix est d'utiliser un vrai navigateur. En pratique, les environnements d'exécution qui prennent en charge le navigateur démarrent et contrôlent les instances d'un vrai navigateur, même s'ils "démarrent" dans Node.js.

Contrôler un navigateur dans le cadre d'un test signifie qu'il s'ouvrira comme il le ferait pour un utilisateur. Vous pourrez ainsi le contrôler en chargeant des URL, du code HTML et JavaScript personnalisé, ou tout autre élément nécessaire à l'exécution du test. Vous pouvez ensuite écrire du code pour agir en tant qu'utilisateur, par exemple en contrôlant la souris ou en saisissant des données dans des zones de saisie.

Les outils modernes tels que WebdriverIO ou Web TestRunner peuvent contrôler tous les principaux navigateurs, et même exécuter plusieurs instances en même temps. Ces navigateurs peuvent s'exécuter à côté du lanceur de test (par exemple, sur votre propre ordinateur ou dans le cadre d'une action CI) ou être externalisés vers des services commerciaux externes qui les exécuteront pour vous.

Les bibliothèques de test plus établies (y compris Vitest et Jest) disposent souvent d'un mode navigateur. Toutefois, comme leur origine provient de Node.js, leurs modes de navigateur sont souvent "intégrés" et n'incluent pas certaines fonctionnalités utiles. Par exemple, Vitest ne peut pas simuler les importations de modules dans le navigateur, ce qui est une primitive puissante que nous utilisons dans l'exemple de la page suivante.

En pratique

À mesure que vos tests deviennent plus complexes, il devient de plus en plus important d'utiliser un vrai navigateur.

  • L'environnement n'a pas d'importance pour les tests qui n'utilisent aucune fonctionnalité ou minimale du DOM, même les fonctionnalités disponibles dans Node.js et des environnements d'exécution similaires, tels que fetch ou EventTarget.
  • JSDOM peut être adapté aux tests de composants de petite envergure.
  • Les tests plus importants, par exemple les tests de bout en bout, qui peuvent simuler la connexion d'un utilisateur et l'exécution d'une action de base, peuvent s'exécuter entièrement dans un navigateur réel.

Cette section est lourde en théorie et présente différents points de vue sur les emplacements d'exécution des tests. En pratique, votre codebase utilise souvent de nombreuses approches différentes pour différents types de tests, en fonction de vos besoins et de ce que fournissent les outils de test.

Testez vos connaissances

Quelles fonctionnalités du navigateur le fichier jsdom de la couche d'émulation ne prend-il *pas* en charge ?

Moteur de mise en page
JSDOM n'est pas un outil visuel. Il ne peut donc pas être utilisé pour vérifier la position d'un élément sur la page, ses attributs CSS résolus ou toute autre partie de la mise en page d'un site Web.
WebSocket
JSDOM inclut le polyfill WebSocket, ce qui signifie que le code qui l'utilise fonctionne.
requestAnimationFrame
Avec l'indicateur "pretendToBeVisual", jsdom appelle le rappel "animation" à 60 FPS, même si rien n'est réellement dessiné.