Site icon Blog ARC Optimizer

C'est un piège (test frontal) ! Six pièges courants dans les tests et comment les résoudre


À propos de l'auteur

Après son apprentissage en tant que développeur d'applications, Ramona contribue au développement de produits chez shopware AG depuis plus de 5 ans maintenant : First in …

En savoir plus sur

Ramona

Lors de la rédaction de tests frontaux, vous rencontrerez de nombreux pièges en cours de route. En résumé, ils peuvent entraîner une maintenance médiocre, un temps d'exécution lent et, dans le pire des cas, des tests auxquels vous ne pouvez pas faire confiance. Mais il ne doit pas en être ainsi. Dans cet article, je parlerai des erreurs courantes commises par les développeurs, du moins selon mon expérience, et, bien sûr, de la façon de les éviter. Les tests n'ont pas besoin d'être douloureux, après tout.

Alors que je revoyais un film que j'aimais quand j'étais enfant, une citation en particulier est ressortie. C'est tiré du film Star Wars de 1983 " Le Retour du Jedi ". La ligne est dite pendant la bataille d'Endor, où l'Alliance mobilise ses forces dans un effort concentré pour détruire l'étoile de la mort. Là, l'amiral Ackbar, chef des rebelles de Mon Calamari, prononce sa phrase mémorable :

« C'est un piège ! » – Amiral Akbar (Crédit image : Ramona Schwering)

"C'est un piège!" Cette ligne nous alerte d'une embuscade inattendue, d'un danger imminent. D'accord, mais qu'est-ce que cela a à voir avec les tests ? Eh bien, c'est simplement une allégorie appropriée lorsqu'il s'agit de traiter des tests dans une base de code. Ces pièges peuvent ressembler à une embuscade inattendue lorsque vous travaillez sur une base de code, en particulier lorsque vous le faites pendant longtemps.

Dans cet article, je vais vous raconter les pièges que j'ai rencontrés dans ma carrière — dont certains étaient de ma faute. Dans ce contexte, je dois donner un avertissement : mon activité quotidienne est fortement influencée par mon utilisation du framework Jest pour les tests unitaires et par le framework Cypress pour les tests de bout en bout. Je ferai de mon mieux pour que mon analyse reste abstraite, afin que vous puissiez également utiliser les conseils avec d'autres frameworks. Si vous trouvez que ce n'est pas possible, veuillez commenter ci-dessous afin que nous puissions en parler! Certains exemples peuvent même être applicables à tous les types de tests, qu'il s'agisse de tests unitaires, d'intégration ou de bout en bout.

Les pièges des tests frontaux

Les tests, quel qu'ils soient, présentent de nombreux avantages. Les tests frontaux sont un ensemble de pratiques permettant de tester l'interface utilisateur d'une application Web. Nous testons sa fonctionnalité en soumettant son interface utilisateur à un stress permanent. Selon le type de test, nous pouvons y parvenir de différentes manières et à différents niveaux :

  • Les tests unitaires examinent les unités mineures de vos applications. Ces unités peuvent être des classes, des interfaces ou des méthodes. Les tests vérifient s'ils donnent la sortie attendue, en utilisant des entrées prédéfinies — ainsi, testant les unités séparément et isolément.
  • Les tests d'intégration ont une portée plus large. Ils testent ensemble des unités de code, en examinant leur interaction.
  • Des tests de bout en bout testent l'application, comme le ferait un utilisateur réel. Ainsi, cela ressemble aux tests système si nous examinons l'assurance qualité en théorie.

Ensemble, faire tout cela peut nous donner beaucoup de confiance dans la livraison de notre application – les tests frontaux garantissent que les gens interagira avec l'interface utilisateur comme nous le souhaitons. D'un autre point de vue, en utilisant ces pratiques, nous sommes en mesure de garantir des versions sans erreur d'une application sans beaucoup de tests manuels, qui consomment des ressources et de l'énergie.

Cette valeur peut cependant être éclipsée, car de nombreux points douloureux ont des causes diverses. Beaucoup d'entre eux pourraient être considérés comme des « pièges ». Imaginez faire quelque chose avec les meilleures intentions, mais cela finit par être douloureux et épuisant : c'est le pire type de dette technique.

Pourquoi devrions-nous nous embêter à tester des pièges ?

Quand je pense aux causes et aux effets de la tests frontaux pièges dans lesquels je suis tombé, certains problèmes me viennent à l'esprit. Trois causes en particulier me reviennent encore et encore, résultant du code hérité que j'avais écrit il y a des années.

  1. Tests lents, ou au moins lenteur de l'exécution des tests.
    Lors du développement local, les développeurs ont tendance à s'impatienter avec les tests. , en particulier si un membre de votre équipe doit fusionner les demandes d'extraction correspondantes. Les longs délais d'attente semblent extrêmement ennuyeux dans tous les cas. Ce piège peut provenir de nombreuses petites causes – par exemple, le fait de ne pas prêter beaucoup d'attention aux temps d'attente appropriés ou à la portée d'un test.
  2. Des tests difficiles à maintenir.
    Ce deuxième point douloureux est encore plus important. critique et une cause plus importante de tests abandonnés. Par exemple, vous pourriez revenir à un test des mois plus tard et ne pas comprendre du tout son contenu ou son intention. Ou les membres de l'équipe pourraient vous demander ce que vous vouliez réaliser avec un ancien test que vous avez écrit. En général, trop de classes ou d'abstractions éparpillées sur des murs de texte ou de code peuvent rapidement tuer la motivation d'un développeur et conduire à un chaos pur et simple. Les pièges dans ce domaine peuvent être causés par le respect des meilleures pratiques qui ne conviennent pas aux tests.
  3. Des tests qui ne vous donnent aucune valeur cohérente.
    Vous pouvez appeler ces Heisenfails ou Heisentests, comme le célèbre Heisenbug, qui ne se produit que si vous détournez le regard, ne le mesurez pas ou, dans notre cas, ne le déboguez pas. Le pire des cas est un test floconneuxun test non déterminant qui ne parvient pas à fournir le même résultat entre les versions sans aucun changement. Cela peut se produire pour diverses raisons, mais cela se produit généralement lorsque vous essayez de prendre un raccourci simple et apparemment pratique, sans tenir compte des meilleures pratiques de test.

Mais ne vous inquiétez pas trop de mes propres expériences. Tester et gérer les tests peut être amusant ! Nous avons juste besoin de garder un œil sur certaines choses pour éviter une issue douloureuse. Bien sûr, la meilleure chose à faire est d'éviter les pièges dans nos conceptions de test en premier lieu. Mais si le mal est déjà fait, refactoriser une base de test est la meilleure chose à faire.

La règle d'or

Supposons que vous travaillez sur un travail passionnant mais exigeant. Vous vous y concentrez entièrement. Votre cerveau est plein de code de production, sans espace libre pour toute complexité supplémentaire, surtout pas pour les tests. Prendre beaucoup d'espace libre est tout à fait contraire au but des tests. Dans le pire des cas, les tests qui semblent être un fardeau sont une raison pour laquelle de nombreuses équipes les abandonnent.

Dans son guide « JavaScript Testing Best Practices », Yoni Goldberg énonce la règle d'or pour empêcher les tests de se sentir comme un fardeau : un test doit ressembler à un assistant amical, là pour vous aider, et ne doit jamais être un obstacle.

Je suis d'accord. C'est la chose la plus cruciale dans les tests. Mais comment y parvenir, exactement ? Petite alerte spoiler : la plupart de mes exemples illustreront cela. Le principe KISS (restez simple, stupide) est la clé. Tout test, quel que soit son type, doit être conçu de manière claire et simple.

Alors, qu'est-ce qu'un test simple et clair ? Comment saurez-vous si votre test est assez simple ? Ne pas compliquer vos tests est de la plus haute importance. L'objectif principal est parfaitement résumé par Yoni Goldberg :

« Il faut regarder un test et obtenir l'intention instantanément. »

Ainsi, la conception d'un test doit être plate. Minimaliste le décrit le mieux. Un test ne devrait pas avoir beaucoup de logique et peu ou pas d'abstractions du tout. Cela signifie également que vous devez être prudent avec les objets de page et les commandes, et que vous devez nommer et documenter de manière significative les commandes. Si vous avez l'intention de les utiliser, faites attention aux commandes, fonctions et noms de classe indicatifs. De cette façon, un test restera un plaisir pour les développeurs et les testeurs.

Mon principe de test préféré concerne la duplication, le principe DRY : Ne vous répétez pas. Si l'abstraction entrave la compréhension de votre test, évitez complètement le code en double.

Cet extrait de code est un exemple :

// Cypress
avantChaque(() => {
    // Il est difficile de voir au premier coup d'œil ce que ces
    // la commande fait vraiment
    cy.setInitialState()
       .then(() => {
           retourne cy.login();
       })
}):

Pour rendre le test plus compréhensible, vous pourriez penser qu'il ne suffit pas de nommer les commandes de manière significative. Au lieu de cela, vous pouvez également envisager de documenter les commandes dans les commentaires, comme ceci :

// Cypress
/**
* Se connecte silencieusement à l'aide de l'API
* @memberOf Cypress.Chainable#
* @name loginViaApi
* @une fonction
*/
Cypress.Commands.add('loginViaApi', () => {
   return cy.authenticate().then((résultat) => {
       return cy.window().then(() => {
           cy.setCookie('bearerAuth', résultat);
       }).then(() => {
           cy.log('Les appareils sont créés.');
       });
   });
});

Une telle documentation peut être essentielle dans ce cas, car elle vous aidera à mieux comprendre le test et votre équipe future . Vous voyez, certaines bonnes pratiques pour le code de production ne conviennent pas au code de test. Les tests ne sont tout simplement pas du code de production, et nous ne devons jamais les traiter comme tels. Bien sûr, nous devons traiter le code de test avec le même soin que le code de production. Cependant, certaines conventions et meilleures pratiques peuvent entrer en conflit avec la compréhension. Dans de tels cas, souvenez-vous de la règle d'or et privilégiez l'expérience du développeur.

Pièges dans la conception des tests

Dans les premiers exemples de cette section, je vais expliquer comment éviter de tomber dans les pièges des tests dans le premier endroit. Après cela, je parlerai de la conception des tests. Si vous travaillez déjà sur un projet de longue date, cela devrait toujours être utile.

La règle de trois

Commençons par l'exemple ci-dessous. Faites attention à son titre. Le contenu du test lui-même est secondaire.

// Jest
describe('deprecated.plugin', () => {
    it('devrait lancer une erreur',() => {
       // Test réel, raccourci pour le lancement de composant
        // une erreur
        composant const = createComponent();

        expect(global.console.error).toBeCalled();
    });
});

En regardant ce test, pouvez-vous dire à première vue ce qu'il est censé accomplir ? En particulier, imaginez que vous regardez ce titre dans vos résultats de test (par exemple, vous pouvez consulter les entrées de journal dans vos pipelines en intégration continue). Eh bien, cela devrait générer une erreur, évidemment. Mais de quelle erreur s'agit-il ? Dans quelles circonstances doit-il être jeté ? Vous voyez, comprendre à première vue ce que ce test est censé accomplir n'est pas facile parce que le titre n'est pas très significatif.

Rappelez-vous notre règle d'or, que nous devrions savoir instantanément à quoi le test est destiné. fais. Donc, nous devons changer cette partie de celui-ci. Heureusement, il existe une solution facile à comprendre. Nous intitulerons ce test avec la règle de trois.

Cette règle, introduite par Roy Osherovevous aidera à clarifier ce qu'un test est censé accomplir. C'est une pratique bien connue dans les tests unitaires, mais elle serait également utile dans les tests de bout en bout. Selon la règle, le titre d'un test doit comprendre trois parties :

  1. Qu'est-ce qui est testé ?
  2. Dans quelles circonstances serait-il testé ?
  3. Quel est le résultat attendu ?

D'accord, quel serait notre test ressemble si nous avons suivi cette règle? Voyons :

// Jest
describe('deprecated.plugin', () => {
it('Propriété : devrait générer une erreur si le fichier obsolète
         prop est utilisé', () => {
       // Test réel, raccourci pour le lancement de composant
        // une erreur
        composant const = createComponent();

        expect(global.console.error).toBeCalled();
   });
});

Oui, le titre est long, mais vous y trouverez les trois parties :

  1. Qu'est-ce qui est testé ? Dans ce cas, c'est la propriété.
  2. Dans quelles circonstances ? Nous voulons tester une propriété obsolète.
  3. À quoi nous attendons-nous ? L'application devrait générer une erreur.

En suivant cette règle, nous pourrons voir le résultat du test à première vue, pas besoin de lire les journaux. Ainsi, nous sommes en mesure de suivre notre règle d'or dans ce cas.

« Arrange, Act, Assert » vs. « Given, When, Then »

Un autre piège, un autre exemple de code. Comprenez-vous le test suivant en première lecture ?

// Jest
décrire('menu contextuel', () => {
   it('devrait ouvrir le menu contextuel au clic', async () => {
        const contextButtonSelector = 'sw-context-button';
        const contextButton =
              wapper.find(contextButtonSelector);
        wait contextButton.trigger('click');
        const contextMenuSelector = '.sw-context-menu';
        let contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(false);
        contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(true);
   });
});

Si vous le faites, alors félicitations ! Vous êtes remarquablement rapide dans le traitement des informations. Si vous ne le faites pas, ne vous inquiétez pas ; c'est tout à fait normal, car la structure du test pourrait être grandement améliorée. Par exemple, les déclarations et les assertions sont écrites et mélangées sans aucune attention à la structure. Comment pouvons-nous améliorer ce test ?

Il existe un modèle qui pourrait être utile, le modèle AAA. AAA est l'abréviation de « organiser, agir, affirmer », qui vous dit quoi faire pour structurer clairement un test. Divisez le test en trois parties importantes. Convenant à des tests relativement courts, ce modèle est principalement rencontré dans les tests unitaires. En bref, ce sont les trois parties :

  • Disposer
    Ici, vous configureriez le système testé pour atteindre le scénario que le test vise à simuler. Cela pourrait impliquer n'importe quoi, de la configuration de variables au travail avec des simulations et des stubs.
  • Act
    Dans cette partie, vous exécuteriez l'unité sous le test. Ainsi, vous feriez toutes les étapes et tout ce qui doit être fait pour arriver à l'état de résultat du test.
  • Assert
    Cette partie est relativement explicite. Vous feriez simplement vos affirmations et vos vérifications dans cette dernière partie.

C'est une autre façon de concevoir un test d'une manière simple et compréhensible. Avec cette règle à l'esprit, nous pourrions changer notre test mal écrit comme suit :

// Jest
décrire('menu contextuel', () => {
    it('devrait ouvrir le menu contextuel au clic', () => {
        // Organiser
        const contextButtonSelector = 'sw-context-button';
        const contextMenuSelector = '.sw-context-menu';

        // Affirmation de l'état avant le test
        let contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(false);

        // Agir
        const contextButton =
             wapper.find(contextButtonSelector);
        wait contextButton.trigger('click');

        // Affirmer
        contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(true);
    });
});

Mais attendez ! Quelle est cette partie d'agir avant d'affirmer ? Et pendant que nous y sommes, ne pensez-vous pas que ce test a un peu trop de contexte, étant un test unitaire ? Corriger. Nous traitons ici des tests d'intégration. Si nous testons le DOM, comme nous le faisons ici, nous devrons vérifier les états avant et après. Ainsi, alors que le modèle AAA est bien adapté aux tests unitaires et API, il ne l'est pas dans ce cas.

Regardons le modèle AAA du point de vue suivant. Comme Claudio Lassala le déclare dans l'un de ses billets de blogau lieu de penser à comment je vais…

  • “… organiser mon test, je pense ce que je suis donné."
    Ceci est le scénario avec toutes les conditions préalables du test.
  • "…act dans mon test, je pense quand quelque chose se passe .”
    Ici, nous voyons les actions du test.
  • “… affirment les résultats, je pense que si quelque chose se produit alors c'est ce que j'attends comme le résultat. "
    Ici, nous trouvons les choses que nous voulons affirmer, étant l'intention du test.

Les mots-clés en gras dans le dernier point font allusion à un autre modèle du développement axé sur le comportement (BDD). C'est le modèle donné-quand-alorsdéveloppé par Daniel Terhorst-North et Chris Matts. Vous connaissez peut-être celui-ci si vous avez rédigé des tests dans la langue Gherkin :

Fonctionnalité : menu contextuel
  Scénario:
    Étant donné que j'ai un sélecteur pour le menu contextuel
       Et j'ai un sélecteur pour le bouton contextuel

    Lorsque le menu contextuel peut être trouvé
       Et ce menu est visible
       Et ce bouton contextuel peut être trouvé
       Et est cliqué
     
   Ensuite, je devrais pouvoir trouver le contextMenu dans le DOM
      Et ce menu contextuel est visible

Cependant, vous pouvez l'utiliser dans toutes sortes de tests, par exemple en structurant des blocs. En utilisant l'idée des puces ci-dessus, la réécriture de notre exemple de test est assez simple :

// Jest
décrire('menu contextuel', () => {
    it('devrait ouvrir le menu contextuel au clic', () => {
        // Étant donné
        const contextButtonSelector = 'sw-context-button';
        const contextMenuSelector = '.sw-context-menu';

        // Lorsque
        let contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(false);
        const contextButton =
             wapper.find(contextButtonSelector);
        wait contextButton.trigger('click');

        // Puis
        contextMenu = wrapper.find(contextMenuSelector);
        expect(contextMenu.isVisible()).toBe(true);
    });
});

Données que nous avions l'habitude de partager

Nous avons atteint le prochain piège. L'image ci-dessous semble paisible et heureuse, deux personnes partageant un papier :

Données de test que nous avions l'habitude de partager. (Crédit image : Ramona Schwering)

Cependant, ils pourraient avoir un réveil brutal. Appliquez cette image à un test, avec les deux personnes représentant les tests et le papier représentant les données du test. Nommons ces deux tests, test A et test B. Très créatif, non ? Le fait est que le test A et le test B partagent les mêmes données de test ou, pire, s'appuient sur un test précédent.

Ceci est problématique car cela conduit à des tests floconneux. Par exemple, si le test précédent échoue ou si les données de test partagées sont corrompues, les tests eux-mêmes ne peuvent pas s'exécuter avec succès. Un autre scénario serait que vos tests soient exécutés dans un ordre aléatoire. Lorsque cela se produit, vous ne pouvez pas prédire si le test précédent restera dans cet ordre ou sera terminé après les autres, auquel cas les tests A et B perdraient leur base. Cela ne se limite pas non plus aux tests de bout en bout ; un cas typique dans les tests unitaires est celui de deux tests mutant les mêmes données de départ.

Très bien, regardons un exemple de code d'un test de bout en bout de mes activités quotidiennes. Le test suivant couvre la fonctionnalité de connexion d'une boutique en ligne.

// Cypress
describe('Connexion client', () => {

    // Exécuté avant chaque test
    avantChaque(() => {
        // Étape 1 : Mettre l'application en état de nettoyage
        cy.setInitialState()
           .then(() => {
             // Étape 2 : Créer des données de test
             return cy.setFixture('client');
           })
            // … utiliser cy.request pour créer le client
    }):

    // … les tests commenceront ci-dessous
})

Pour éviter les problèmes mentionnés ci-dessus, nous exécuterons le hook beforeEach de ce test avant chaque test dans son fichier. Là, la première et la plus cruciale étape que nous allons prendre est de réinitialiser notre application à ses paramètres d'usine, sans aucune donnée personnalisée ou quoi que ce soit. Notre objectif ici est de s'assurer que tous nos tests ont la même base. De plus, il protège ce test de tout effet secondaire en dehors du test. Fondamentalement, nous l'isolons, en éloignant toute influence de l'extérieur.

La deuxième étape consiste à créer toutes les données nécessaires pour exécuter le test. Dans notre exemple, nous devons créer un client qui peut se connecter à notre boutique. Je veux créer toutes les données dont le test a besoin, adaptées spécifiquement au test lui-même. De cette façon, le test sera indépendant, et l'ordre d'exécution peut être aléatoire. Pour résumer, les deux étapes sont essentielles pour s'assurer que les tests sont isolés de tout autre test ou effet secondaire, maintenant ainsi la stabilité.

Implementation Traps

Très bien, nous avons parlé de la conception des tests. Parler de bonne conception de test ne suffit pas, car le diable est dans les détails. Inspectons donc nos tests et défions l'implémentation réelle de notre test.

Foo Bar Quoi ?

Pour ce premier piège dans l'implémentation des tests, nous avons un invité ! C'est BB-8, et il a trouvé quelque chose dans l'un de nos tests :

Que veut dire « Foo Bar » ?! (Crédit image : Ramona Schwering)

Il a trouvé un nom qui nous est peut-être familier mais pas : Foo Bar. Bien sûr, nous, les développeurs, savons que Foo Bar est souvent utilisé comme nom d'espace réservé. Mais si vous le voyez dans un test, saurez-vous immédiatement ce qu'il représente ? Encore une fois, le test peut être plus difficile à comprendre à première vue.

Heureusement, ce piège est facile à résoudre. Regardons le test Cypress ci-dessous. C'est un test de bout en bout, mais les conseils ne se limitent pas à ce type.

// Cypress
it('devrait créer et lire le produit', () => {
    // Ouvrir le module pour ajouter un produit
    cy.get('a[href="#/sw/product/create"]').click();

    // Ajouter des données de base au produit
    cy.get('.sw-field—nom-du-produit').type('T-Shirt Ackbar');
    cy.get('.sw-select-product__select_manufacturer')
        .type('Entreprise spatiale');

    // … le test continue …
});

Ce test est censé vérifier si un produit peut être créé et lu. Dans ce test, je souhaite simplement utiliser des noms et des espaces réservés liés à un produit réel :

  • Pour le nom d'un produit t-shirt, je souhaite utiliser « T-Shirt Akbar ».
  • Pour le nom du fabricant, « Space Company » est une idée.

Vous n'avez cependant pas besoin d'inventer tous les noms de produits. Vous pouvez générer automatiquement des données ou, encore plus joliment, les importer depuis votre état de production. Quoi qu'il en soit, je veux m'en tenir à la règle d'or, même lorsqu'il s'agit de nommer.

Regardez les sélecteurs, vous devez

Nouveau piège, même test. Regarde-le, tu remarques quelque chose ?

// Cypress
it('devrait créer et lire le produit', () => {
    // Ouvrir le module pour ajouter un produit
    cy.get('a[href="#/sw/product/create"]').click();

    // Ajouter des données de base au produit
    cy.get('.sw-field—nom-du-produit').type('T-Shirt Ackbar');
    cy.get('.sw-select-product__select_manufacturer')
        .type('Entreprise spatiale');

    // … Le test continue …
});

Avez-vous remarqué ces sélecteurs ? Ce sont des sélecteurs CSS. Eh bien, vous vous demandez peut-être : « Pourquoi sont-ils problématiques ? Ils sont uniques, ils sont faciles à manipuler et à entretenir, et je peux les utiliser parfaitement ! Cependant, êtes-vous sûr que c'est toujours le cas ?

La vérité est que les sélecteurs CSS sont susceptibles de changer. Si vous refactorisez et, par exemple, changez de classe, le test peut échouer, même si vous n'avez pas introduit de bogue. Une telle refactorisation est courante, de sorte que ces échecs peuvent être ennuyeux et épuisants pour les développeurs à corriger. Donc, gardez à l'esprit qu'un test qui échoue sans bogue est un faux positif, ne donnant aucun rapport fiable pour votre application.

« Regardez les sélecteurs que vous devez ! " (Crédit image : Ramona Schwering)

Ce piège fait principalement référence aux tests de bout en bout dans ce cas. Dans d'autres circonstances, cela pourrait également s'appliquer aux tests unitaires, par exemple, si vous utilisez des sélecteurs dans les tests de composants. Comme le déclare Kent C. Dodds dans son article sur le sujet :

« Vous ne devriez pas tester les détails de l'implémentation ».

À mon avis, il existe de meilleures alternatives à l'utilisation des détails de l'implémentation pour les tests. Au lieu de cela, testez les choses qu'un utilisateur remarquerait . Mieux encore, choisissez des sélecteurs moins susceptibles de changer. Mon type de sélecteur préféré est l'attribut data. Un développeur est moins susceptible de modifier les attributs de données lors de la refactorisation, ce qui les rend parfaits pour localiser des éléments dans les tests. Je recommande de les nommer de manière significative pour clairement indiquer leur objectif à tous les développeurs travaillant sur le code source. Cela pourrait ressembler à ceci :

// Cypress
cy.get('[data-test=sw-field—product-name]')
  .type('T-shirt Ackbar');
cy.get('[data-test=sw-select-product__select_manufacturer]')
  .type('Entreprise spatiale');

Les faux positifs ne sont qu'un des problèmes auxquels nous nous heurtons lorsque nous testons les détails de l'implémentation. Le contraire, des faux négatifs, peut également se produire lors du test des détails de mise en œuvre. Un faux positif se produit lorsqu'un test réussit même lorsque l'application a un bogue. Le résultat est que les tests à nouveau consomment de l'espace libre, en contradiction avec notre règle d'or. Nous devons donc éviter cela autant que possible.

Note : Ce sujet est énorme, il serait donc préférable de le traiter dans un autre article. Jusque-là, je vous suggère de consulter l'article de Dodds sur « Testing Implementation Details » pour en savoir plus sur le sujet.

Attendez-le !

Last but not least, il s'agit d'un sujet sur lequel je ne saurais trop insister. Je sais que ce sera ennuyeux, mais je vois encore beaucoup de gens le faire, donc je dois le mentionner ici comme un piège.

C'est le problème du temps d'attente fixe dont j'ai parlé dans mon article sur les tests floconneux. Jetez un œil à ce test :

// Cypress
Cypress.Commands.add('typeSingleSelect', {
        prevSubject : 'élément',
    },
    (sujet, valeur, sélecteur) => {
    cy.wrap(sujet).should('be.visible');
    cy.wrap(sujet).click();

    cy.attente(500);
    cy.get(`${sélecteur} entrée`)
      .type(valeur);
});

La petite ligne avec cy.wait(500) est un temps d'attente fixe qui interrompt l'exécution du test pendant une demi-seconde. En aggravant cette erreur, vous la trouverez dans une commande personnalisée, de sorte que le test utilisera cette attente plusieurs fois. Le nombre de secondes s'additionnera à chaque utilisation de cette commande. Cela ralentira beaucoup trop le test et ce n'est pas du tout nécessaire. Et ce n'est même pas le pire. Le pire, c'est que nous attendrons trop peu de temps, donc notre test s'exécutera plus rapidement que notre site Web ne pourra y réagir. Cela provoquera des desquamations, car le test échouera parfois. Heureusement, nous pouvons faire beaucoup de choses pour éviter les temps d'attente fixes.

Tous les chemins mènent à une attente dynamique. Je suggérerais de privilégier les méthodes plus déterministes fournies par la plupart des plates-formes de test. Examinons de plus près mes deux méthodes préférées.

  • Attendez les changements dans l'interface utilisateur.
    Ma première méthode de choix est d'attendre les changements dans l'interface utilisateur de l'application qu'un utilisateur humain remarquerait ou même réagirait. . Les exemples peuvent inclure une modification de l'interface utilisateur (comme une flèche de chargement qui disparaît), l'attente de l'arrêt d'une animation, etc. Si vous utilisez Cypress, cela pourrait ressembler à ceci :
    // Cypress
    cy.get('data-cy="submit"').should('be.visible');
    

    Presque tous les frameworks de test offrent de telles possibilités d'attente.

  • Attente des requêtes API.
    Une autre possibilité que j'ai appris à aimer est d'attendre les requêtes API et leurs réponses, respectivement. Pour ne citer qu'un exemple, Cypress fournit des fonctionnalités intéressantes pour cela. Au début, vous définiriez une route que Cypress devrait attendre :
    // Cypress
    cy.intercepter({
        URL : '/widgets/checkout/info',
        méthode : 'GET'
    }).as('checkoutAvailable');
    

    Par la suite, vous pouvez l'affirmer dans votre test, comme ceci :

    // Cypress
    cy.wait('@request').its('response.statusCode')
      .should('égal', 200);
    

    De cette façon, votre test restera stable et fiable, tout en gérant efficacement le temps. De plus, le test pourrait être encore plus rapide car il n'attend que le temps nécessaire.

Principaux points à retenir

Revenant à l'amiral Akbar et à Star Wars en général, la bataille d'Endor s'est avérée être un succès, même s'il a fallu beaucoup de travail pour remporter cette victoire. Avec un travail d'équipe et quelques contre-mesures, cela a été possible et est finalement devenu une réalité.

Appliquez cela aux tests. Cela peut demander beaucoup d'efforts pour éviter de tomber dans un piège de test ou pour résoudre un problème si le dommage est déjà fait, en particulier avec le code hérité. Très souvent, vous et votre équipe aurez besoin d'un changement d'état d'esprit avec la conception de tests ou même de nombreuses refactorisations. Mais cela en vaudra la peine à la fin, et vous finirez par voir les récompenses.

La chose la plus importante à retenir est la règle d'or dont nous avons parlé plus tôt. La plupart de mes exemples le suivent. Tous les points douloureux découlent de son ignorance. Un test doit être un assistant amical, pas un obstacle ! C'est la chose la plus critique à garder à l'esprit. Un test doit donner l'impression de suivre une routine, et non de résoudre une formule mathématique complexe. Faisons de notre mieux pour y parvenir.

Vous voyez, R2-D2 détecte facilement les bogues ici. Je veux que vous et votre équipe vous sentiez comme ça aussi, alors rendons les tests légers et amusants ! (Crédit image : Ramona S.)

J'espère avoir pu vous aider en vous donnant quelques idées sur les pièges les plus courants que j'ai rencontrés. Cependant, je suis sûr qu'il y aura beaucoup plus de pièges à trouver et à apprendre. Je serais si heureux si vous partagiez les pièges que vous avez rencontrés le plus dans les commentaires ci-dessous, afin que nous puissions tous apprendre de vous également. À bientôt !

Autres ressources

(vf, il, al)




Source link
Quitter la version mobile