Français
  • test
  • test e2e
  • test d'intégration
  • dev
  • productivité
  • jest
  • puppeteer

Un guide rapide pour écrire des tests de bout en bout avec jest-puppeteer

Cet article propose un guide rapide pour écrire des tests de bout en bout efficaces avec jest-puppeteer, en mettant l'accent sur le processus de configuration, les API couramment utilisées et des scénarios de test pratiques en utilisant une simple application de tâches à faire comme exemple.

Yijun
Yijun
Developer

Dans le cadre de notre engagement à assurer la qualité de Logto et l'amélioration continue, nous utilisons jest-puppeteer pour les tests automatisés de bout en bout. Cela nous permet d'itérer rapidement sur le développement de Logto sans interruptions.

Dans cet article, nous partagerons notre expérience de l'écriture de scripts de test jest-puppeteer efficaces en utilisant une simple application de tâches à faire comme exemple. Notre objectif est de vous aider à commencer rapidement à écrire du code de test jest-puppeteer pour vos propres projets.

Introduction aux tests de bout en bout avec jest-puppeteer

Les tests de bout en bout sont un moyen de s'assurer que votre application fonctionne correctement du point de vue de l'utilisateur. Pour y parvenir, nous utilisons deux outils essentiels : Jest et Puppeteer.

Jest est un cadre de test JavaScript populaire qui offre une API conviviale pour écrire des tests et des assertions. Il est largement utilisé pour les tests unitaires et d'intégration.

Puppeteer est une bibliothèque Node.js développée par l'équipe Chrome qui fournit une API de haut niveau pour contrôler les navigateurs Chrome ou Chromium sans tête. Cela en fait un choix idéal pour automatiser les interactions du navigateur dans les tests de bout en bout.

jest-puppeteer est un pré-réglage de Jest qui permet les tests de bout en bout avec Puppeteer. Il propose une API simple pour lancer de nouvelles instances de navigateur et interagir avec les pages Web à travers celles-ci.

Maintenant que vous avez une compréhension de base des outils essentiels, plongeons dans la configuration de votre environnement de test.

Configuration de votre environnement de test

La configuration de votre environnement de test pour les tests de bout en bout avec jest-puppeteer est un processus simple qui implique trois étapes principales:

  1. Installer les dépendances

Dans votre projet (ou créer un nouveau projet), ouvrez le terminal et exécutez :

Puis allez dans le node_modules/puppeteer et installez Chromium (qui est nécessaire pour Puppeteer) :

  1. Configurer Jest

Ensuite, vous devez configurer Jest pour qu'il fonctionne parfaitement avec Puppeteer.

Créez un fichier de configuration Jest (par exemple, jest.config.js) dans le répertoire racine de votre projet si vous n'en avez pas déjà un. Dans ce fichier de configuration, spécifiez jest-puppeteer comme pré-réglage :

Vous pouvez personnaliser d'autres paramètres Jest dans ce fichier selon vos besoins. Pour plus d'informations sur la personnalisation des configurations Jest, veuillez vous référer à la configuration Jest.

  1. Écrire vos tests

Créez des fichiers de test dans votre projet, généralement nommés avec l'extension .test.js. Jest découvrira et exécutera automatiquement ces fichiers de test.

Voici un exemple tiré de la Documentation de Jest:

Exécutez ensuite la commande pour lancer le test :

En suivant ces trois étapes, vous aurez un environnement de test bien configuré pour effectuer des tests de bout en bout utilisant jest-puppeteer.

Cependant, veuillez noter que ceci n'est qu'un exemple de base. Pour plus d'informations détaillées sur la configuration de l'environnement, veuillez vous référer à la documentation pertinente :

API couramment utilisées

Dans les étapes à venir, nous nous appuierons sur les API fournies par Jest, Puppeteer, et jest-puppeteer pour nous aider dans nos tests.

Jest offre principalement des API pour organiser les tests et affirmer les résultats attendus. Vous pouvez explorer les détails spécifiques dans la documentation.

Les API de Puppeteer sont principalement conçues pour interagir avec les navigateurs, et leur support pour les tests peut ne pas être aussi direct. Vous pouvez vous référer à la documentation de Puppeteer pour comprendre les fonctionnalités qu'il fournit. Dans nos exemples de tests ultérieurs, nous couvrirons quelques cas d'utilisation courants.

Comme les API de Puppeteer n'ont pas été conçues à l'origine pour les tests, les utiliser pour écrire des tests peut être un défi. Pour simplifier le processus d'écriture des tests Puppeteer, jest-puppeteer inclut une bibliothèque intégrée appelée expect-puppeteer. Il offre un ensemble d'API concises et conviviales. La bibliothèque est détaillée dans sa documentation, qui introduit diverses fonctionnalités utiles, comme le montrent les exemples ci-dessous :

Dans les exemples suivants, nous allons combiner les API fournies par ces bibliothèques pour compléter nos scénarios de test.

Il est maintenant temps de commencer à apprendre à écrire du code de test en utilisant notre simple application de tâches à faire.

L'application simple de tâches à faire

En supposant que nous ayons une simple application de tâches à faire fonctionnant à http://localhost:3000, le code HTML principal de cette application est le suivant :

Lors de la réalisation de tests de bout en bout, nous visons à couvrir les scénarios suivants :

  1. Lorsque nous accédons à l'URL de l'application, l'application et ses données doivent se charger correctement.
  2. Le bouton de vérification de l'article doit être capable de cliquer.
  3. Cliquer sur un lien externe à l'intérieur des notes de l'article doit ouvrir le lien dans un nouvel onglet.
  4. Peut ajouter un article à partir du formulaire.

Plus tard, nous écrirons du code de test pour valider ces scénarios.

Attendre que l'application et ses données soient chargées

Nous considérons que l'application a réussi à charger lorsque les conditions suivantes sont remplies :

  1. Un élément avec l'ID "app" doit exister sur la page après avoir accédé à l'URL de l'application.
  2. Les données à l'intérieur de l'application doivent être rendues correctement.

Donc, nous avons écrit le code de test suivant :

Dans le code :

  • page.goto est équivalent à entrer "http://localhost:3000" dans le navigateur, ce qui vous permet de naviguer vers n'importe quelle URL.
  • page.waitForSelector est utilisé pour attendre qu'un sélecteur CSS spécifique corresponde à un élément particulier, avec un temps d'attente par défaut de 30 secondes.
  • expect(page).toMatchElement provient de la lib expect-puppeteer, Il est similaire à page.waitForSelector, mais il supporte également la correspondance du texte à l'intérieur d'un élément. De plus, le délai d'attente par défaut pour toMatchElement n'est que de 500ms.

Cela peut sembler parfait au premier abord, mais lors de l'exécution de ce test et de son déploiement dans un environnement CI, il échoue occasionnellement après plusieurs exécutions. Le message d'échec indique :

Sur la base de l'information d'échec et du fait que ce test réussit la plupart du temps, nous pouvons en déduire que les données demandées par l'application peuvent ne pas toujours revenir dans les 500ms suivant le chargement de l'application. Par conséquent, nous voulons attendre que les données soient chargées avant de faire l'assertion.

Il y a typiquement deux approches courantes pour y parvenir :

  1. Augmenter le temps d'attente de l'assertion

À partir du message d'erreur, nous pouvons voir que le temps d'attente par défaut pour toMatchElement est fixé à 500ms. Nous pouvons augmenter le temps d'attente en ajoutant l'option timeout à la fonction de cette manière :

Cette approche peut réduire l'occurrence des tests ratés dans une certaine mesure, mais elle ne résout pas complètement le problème car nous ne pouvons pas savoir combien de temps est nécessaire pour que les données soient récupérées.

C'est pourquoi nous utilisons cette approche uniquement lorsque nous sommes certains du temps d'attente requis, comme dans des scénarios du type "une info-bulle apparaît après avoir survolé un élément pendant plus de 2 secondes.”

  1. Attendre la fin de la requête réseau avant de faire une assertion

C'est l'approche correcte. Bien que nous ne sachions peut-être pas combien de temps la demande de données prendra, attendre la fin de la demande réseau est toujours un choix sûr. À ce stade, nous pouvons utiliser page.waitForNavigation({ waitUntil: 'networkidle0' }) pour attendre la fin des requêtes réseau :

De cette façon, avant d'exécuter l'assertion pour l'Application et ses données chargées, nous pouvons nous assurer que nos requêtes réseau ont déjà été conclues. Cela assure que le test produit constamment les bons résultats.

Attendre pour cliquer sur un bouton spécifique

Ensuite, nous testons la fonctionnalité de cliquer sur le bouton de vérification à l'intérieur d'un élément.

Dans l'application exemple, nous avons remarqué que tous les éléments partagent la même structure. Leurs boutons de vérification ont le sélecteur CSS li[class$=item] > button, et le texte du bouton est toujours "Vérifier". Cela signifie que nous ne pouvons pas spécifier directement quel bouton de vérification de l'élément à cliquer. Par conséquent, nous avons besoin d'une nouvelle solution.

Supposons que nous voulions cliquer sur le bouton de vérification de l'élément "Lire le document de démarrage de Logto". Nous pouvons décomposer cela en deux étapes :

  1. D'abord, obtenir une référence à cet élément spécifique.
  2. Ensuite, cliquer sur le bouton de vérification situé à l'intérieur de cet élément.

Comme indiqué dans le code, nous utilisons la fonction toMatchElement pour faire correspondre l'élément avec l'itemName défini sur "Lire le document de démarrage de Logto". Ensuite, nous utilisons la référence readDocItem pour cliquer sur le bouton avec le contenu du texte 'Vérifier' situé en dessous de celle-ci.

Il est important de noter que lors de l'obtention de la readDocItem, le sélecteur CSS utilisé est li[class$=item]:has(div[class$=itemName]). Ce sélecteur correspond à l'élément li racine de l'élément, et non à l'itemName à l'intérieur de celui-ci, car nous avons l'intention de cliquer sur le button sous l'étiquette li ultérieurement.

L'utilisation de expect(readDocItem).toClick est similaire à toMatchElement. Dans le code d'exemple, nous passons { text: 'Check' } pour faire correspondre davantage le textContent du bouton. Cependant, vous pouvez choisir de faire correspondre ou non le contenu du texte du bouton en fonction de vos besoins.

Attendre qu'un lien externe soit ouvert dans un nouvel onglet

Ensuite, nous voulons tester si nous pouvons ouvrir un lien externe dans un nouvel onglet en cliquant dessus dans les notes de l'élément.

Dans l'application exemple, nous avons trouvé que l'élément "Lire le document de démarrage de Logto" a un lien externe vers le doc Logto dans ses notes. Voici notre code de test :

Dans le code, nous utilisons toClick pour cliquer sur le lien à l'intérieur de itemNotes.

Par la suite, nous utilisons browser.waitForTarget pour capturer un nouvel onglet ouvert avec l'URL "https://docs.logto.io/docs/tutorials/get-started."

Après avoir obtenu l'onglet, nous utilisons target.page() pour obtenir une instance de la page du doc Logto et vérifier davantage si la page a été chargée.

N'oubliez pas également de fermer la nouvelle page ouverte après avoir terminé notre test.

Attendre pour créer un élément à partir du formulaire

Maintenant, nous voulons tester la fonctionnalité d'ajout d'un élément.

Nous devons remplir le nom de l'élément et les notes de l'élément dans le formulaire, cliquer sur le bouton "Ajouter" et vérifier si le contenu que nous avons ajouté apparaît dans la liste :

Vous remarquerez que nous utilisons la fonction expect(page).toFill(inputSelector, content) pour remplir le contenu du formulaire. Cette fonction remplace tout le contenu de l'entrée par le content.

Si vous voulez ajouter des caractères à l'entrée sans remplacer le contenu existant, vous pouvez utiliser page.type(selector, content) pour ajouter directement le contenu souhaité à l'entrée.

Cependant, lorsque nous avons de nombreux champs à remplir dans notre formulaire, appeler toFill plusieurs fois n'est pas pratique. Dans ce cas, nous pouvons utiliser l'approche suivante pour remplir tout le contenu en un seul appel :

La clé de l'objet fourni est l'attribut name de l'élément input.

Résumé

Nous avons couvert un exemple simple pour introduire les exigences de test communes et les techniques correspondantes d'écriture de code lors de l'utilisation de jest-puppeteer pour les tests de bout en bout. Nous espérons que cela pourra vous aider à comprendre rapidement les bases de l'écriture de code de test jest-puppeteer.