Moins d'un couple. Une autre façon de réduire le nombre de tests

N'importe quel AQ connaît une telle méthode pour minimiser les cas de test que les tests par paires - tests par paires. La méthode est excellente, assez simple et éprouvée par de nombreuses équipes. Mais que faire si, après l'avoir utilisé, il reste trop de boîtiers?



C'est exactement ce qui s'est passé dans mon projet, et aujourd'hui je vais vous dire comment vous pouvez réduire davantage le nombre de cas de test sans perdre en qualité.



image



Objet de test



Tout d'abord, je vais vous parler un peu du produit. Chez Tinkoff, notre équipe a développé des blocs - ce sont des composants React consistant en l'implémentation et la configuration. L'implémentation est le composant lui-même, que nous avons développé et que l'utilisateur voit dans le navigateur. La configuration est JSON qui définit les paramètres et le contenu de cet objet.



La tâche principale des blocs est d'être beaux et d'être affichés de la même manière pour différents utilisateurs. Dans le même temps, le bloc peut changer de manière très significative de la configuration et du contenu.

Par exemple, un bloc peut être comme ceci - sans arrière-plan, avec un bouton et une image à droite:



image



Ou comme ceci - avec un arrière-plan, sans bouton et avec une image à gauche:



image



Ou, en général, comme ceci - avec un lien au lieu d'un bouton et sans liste dans le texte:



image



Tous les exemples ci-dessus sont le même bloc qui a une version de la configuration (une structure JSON que ce composant React particulier peut gérer), mais son contenu différent.



Le circuit lui-même:



{
  components: {
    background: color,

    panel: {
      panelProps: {
        color: {
          style: ['outline', 'color', 'shadow', 'custom'],

          background: color
        },

        size: ['s', 'm', 'l'],

        imagePosition: ['left', 'right']
      },

      title: {
        text: text,

        size: ['s', 'l'],

        htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
      },

      description: {
        text: html,

        htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
      },

      image: {
        alt: text,

        title: text,

        image: {
          src: image,

          srcset: [{
            src: image,

            condition: ['2x', '3x']
          }],

          webpSrcset: [{
            src: image,

            condition: ['1x', '2x', '3x']
          }]
        },

        imageAlign: ['top', 'center', 'bottom']
      },

      button: {
        active: boolean,

        text: text,

        color: {
          style: ['primary', 'secondary', 'outline', 'outlineDark', 'outlineLight', 'textLink', 'custom'],

          backgroundColor: color
        },

        onClick: {
          action: ['goToLink', 'goToBlock', 'showBlock', 'crossSale', 'callFormEvent'],

          nofollow: boolean,

          url: url,

          targetBlank: boolean,

          title: text,

          noindex: boolean,

          guid: guid,

          guidList: [{
            guid: guid
          }],

          formId: guid,

          crossSaleUrl: url,

          eventName: text
        },

        htmlTag: ['div', 'b', 'strong', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']
      },

      href: url
    }
  }
}


Dans ce cas, le bloc avec l'image à droite aura une valeur components.panel.imagePosition = right. Et le bloc avec l'image à gauche a components.panel.imagePosition = left. Pour un bloc avec un bouton - components.button.active = trueet ainsi de suite. J'espère que le principe est clair. C'est ainsi que tous les paramètres de bloc sont définis.



Cas issus d'une combinaison de paramètres



Dans cet article, je n'aborderai pas les questions de gestion des versions de diagramme, les règles de remplissage avec du contenu ou l'origine des données. Ce sont tous des sujets distincts qui n'affectent pas la compilation d'un ensemble de cas de test. La principale chose à savoir: nous avons de nombreux paramètres qui affectent notre composant, et chacun d'eux peut prendre son propre ensemble de valeurs.



image



Pour l'exemple ci-dessus, j'ai choisi un bloc avec une configuration assez simple. Mais même dedans, vérifier toutes les combinaisons des valeurs de tous les paramètres prendra un temps inacceptable, surtout si vous devez prendre en compte la compatibilité entre navigateurs. Habituellement, les tests par paires viennent à la rescousse ici, ou les tests par paires. Des tonnes d'articles ont déjà été écrits sur lui et il y a même une formation . Si vous ne rencontrez pas soudainement, assurez-vous de lire.



Estimons le nombre de cas de test que nous obtiendrons lors de son application. Nous avons plus de 25 paramètres, et certains d'entre eux prennent jusqu'à 7 et 9 variantes de valeurs. Oui, vous pouvez négliger quelque chose: par exemple, si vous vérifiez la mise en page, le guid n'est pas important pour vous. Mais en utilisant le test par paires, vous obtiendrez toujours plus de 80 cas de test. Et cela, comme je l'ai déjà écrit, n'est pas le bloc le plus complexe et sans prendre en compte la compatibilité entre navigateurs. Nous avons maintenant plus de 150 blocs, et leur nombre augmente, nous ne pouvons donc pas nous permettre autant de cas si nous voulons maintenir la vitesse de test et de publication de nouvelles versions.



Cas d'un paramètre



La méthode de test par paires est basée sur l'affirmation selon laquelle la plupart des défauts sont causés par l'interaction d'au plus deux facteurs. Autrement dit, la plupart des bogues se manifestent soit sur une valeur d'un paramètre, soit sur une combinaison des valeurs de deux paramètres. Nous avons décidé d'ignorer la deuxième partie de cette déclaration et avons supposé que la vérification d'un paramètre permettrait de trouver la plupart des bogues.



Ensuite, il s'avère que pour les tests, nous devons vérifier chaque valeur de chaque paramètre au moins une fois. Mais en même temps, chaque bloc porte la configuration entière. Ensuite, dans chaque nouveau cas, vous pouvez vérifier le maximum de valeurs non encore vérifiées afin de minimiser le nombre de cas.



Analysons l'algorithme de construction de cas à l'aide d'un exemple simplifié. Prenons le composant bouton de notre schéma et composons des cas de test pour cela:



 button: {
        active: boolean,

        text: text,

        color: {
          style: ['primary', 'secondary', 'outline', 'custom'],

          backgroundColor: color
        }


Pour simplifier l'exemple, j'ai réduit la longueur de la liste à button.color.style.



Étape 1. Composez les options de contenu pour chaque champ



Tout ici est comme dans les tests par paires: vous devez comprendre quelles valeurs chacun des champs peut prendre. Par exemple, button.activedans notre cas, il ne peut y avoir que deux valeurs: trueou false. Théoriquement, d'autres options peuvent survenir, par exemple l' undefinedabsence de la clé elle-même.



Ici, à mon avis, il est important de définir très clairement les limites et les fonctionnalités de votre système et de ne pas vérifier les choses inutiles. Autrement dit, si la vérification des clés obligatoires ou la validation d'une valeur est implémentée dans un système tiers, cette fonctionnalité doit être vérifiée dans un système tiers. Et nous ne devrions utiliser que des données «correctes» comme cas.



Dans l'ensemble, le même principe est utilisé dans la pyramide des tests. Si vous le souhaitez, les tests d'intégration les plus critiques peuvent nous être ajoutés - par exemple, pour vérifier le traitement d'une clé qui n'est pas arrivée. Mais il devrait y avoir un nombre minimum de ces tests. Une autre approche est la poursuite de tests exhaustifs, ce qui, comme chacun le sait, est impossible.



Nous avons donc identifié les options de contenu pour chaque champ et réalisé le tableau suivant:



image



Ce tableau comprend chaque classe d'équivalence pour chaque paramètre, mais une seule fois.



Voici les classes de valeur dans notre cas, les classes:



  • text_s - chaîne courte;
  • text_m - chaîne plus longue;
  • no_color - pas de couleur;
  • rnd_color est n'importe quelle couleur.


Étape 2. Enrichir le tableau avec des données



Puisque chaque bloc porte la configuration complète, nous devons ajouter des données pertinentes aux cellules vides:



image



maintenant, chaque colonne est un cas.



En même temps, puisque nous sélectionnons nous-mêmes les données manquantes, nous pouvons générer des cas en fonction de la priorité. Par exemple, si nous savons que le texte court est utilisé dans un bouton plus souvent que le texte de longueur moyenne, il vaut la peine de le vérifier plus souvent.



Dans l'exemple ci-dessus, vous pouvez également faire attention aux cas "abandonnés" - cas dans lesquels un paramètre n'est pas du tout vérifié, bien qu'il soit présent dans le tableau. Dans ce cas, button.color.style: secondaryson apparence ne sera pas vérifiée, car le style du bouton désactivé n'a pas d'importance.



Pour éviter que les cas «abandonnés» ne conduisent à des bogues, nous avons utilisé pour analyser les ensembles de valeurs résultants. L'analyse a été effectuée une fois pendant la génération des cas de test, et tous les cas «abandonnés» ont été ajoutés manuellement au cas de test final. Une telle solution au problème est plutôt maladroite, mais bon marché (à moins, bien sûr, que vous ne modifiez rarement la configuration des objets testés).



Une solution plus générale consiste à diviser toutes les valeurs en deux groupes:



  1. les valeurs dangereuses (celles qui peuvent conduire à la «perte» de cas);
  2. sûr (qui ne peut pas conduire à "tomber").


Chaque valeur unsafe est vérifiée dans son propre cas de test, vous pouvez enrichir le cas avec toutes les données sûres. Pour des valeurs sûres, un tableau est compilé selon les instructions ci-dessus.



Étape 3. Clarification des valeurs



Il ne reste plus qu'à générer des valeurs concrètes au lieu de classes d'équivalence.



image



Ici, chaque projet devra choisir ses propres variantes de valeurs, en fonction des caractéristiques de l'objet testé. Certaines valeurs sont très faciles à générer. Par exemple, vous pouvez simplement prendre n'importe quelle couleur pour la plupart des champs. Pour certains blocs, lors de la vérification de la couleur, vous devez ajouter un dégradé, mais il est déplacé vers une classe d'équivalence distincte.



C'est un peu plus compliqué avec du texte: si vous générez une chaîne à partir de caractères aléatoires, les césures, listes, balises, espaces insécables ne seront pas testés. Nous générons des longueurs de ligne courtes et moyennes à partir de texte réel, en le réduisant au nombre de caractères souhaité. Et dans le long texte, nous vérifions:



  • balise html (n'importe laquelle);
  • lien;
  • liste non numérotée.


Cet ensemble de cas découle directement de notre implémentation de bloc. Par exemple, toutes les balises html sont connectées ensemble, il est donc inutile de tester chacune d'elles. Dans ce cas, le lien et la liste sont vérifiés séparément, car ils ont un traitement visuel distinct (mise en évidence au survol et aux fusillades).



Il s'avère que pour chaque projet, vous devez composer votre propre ensemble de contenu en fonction de l'implémentation de l'objet testé.



Algorithme



Bien sûr, à première vue, il peut sembler que l'algorithme est complexe et ne vaut pas la peine. Mais si vous omettez tous les détails et exceptions que j'ai essayé de décrire dans chaque paragraphe ci-dessus, cela s'avère tout simplement.



Étape 1. Ajoutez toutes les valeurs possibles au tableau des paramètres:



image



Étape 2. Dupliquez les valeurs dans des cellules vides:



image



Étape 3. Transformez les valeurs abstraites en valeurs concrètes et obtenez des cas:



image



Chaque colonne du tableau est un cas.



Avantages de l'approche



Cette méthode de génération de cas de test présente plusieurs avantages importants.



image



Moins de cas



D'abord et avant tout, il y a beaucoup moins de cas que dans les tests par paires. Si nous prenons un exemple simplifié avec un bouton, nous avons 4 cas au lieu de 8 dans les tests par paires.



Plus il y a de paramètres dans l'objet testé, plus les économies de cas seront importantes. Par exemple, pour le bloc complet présenté au début de l'article, nous obtenons 11 cas, et avec l'aide de paires - 260.



Le nombre de cas ne gonfle pas avec la complication de la fonctionnalité



Le deuxième avantage est que lorsque de nouveaux paramètres sont pris en compte lors des tests, le nombre de cas n'augmente pas toujours.



Par exemple, ajoutons un paramètre à notre bouton button.color.textColoravec des classes d'équivalence de valeur no_coloret rnd_color. Ensuite, 4 cas resteront, un seul paramètre de plus sera ajouté à chacun d'eux: Le



image



nombre d'observations n'augmentera que si un paramètre a plus de valeurs qu'il n'y en avait.



Vous pouvez vérifier l'important plus souvent



En enrichissant les valeurs (étape 2 de l'algorithme), des valeurs de priorité plus élevée ou plus risquées peuvent être vérifiées plus souvent.



Par exemple, si nous savons que les utilisateurs précédents utilisaient plus souvent du texte plus court et qu'ils utilisent maintenant du texte plus long, nous pouvons enrichir les cas avec du texte plus long et entrer plus souvent dans des cas d'utilisateurs réels.



Peut être automatisé



L'algorithme ci-dessus se prête bien à l'automatisation. Bien sûr, les cas générés par l'algorithme ressembleront moins à des cas réels qu'à des cas générés par l'homme. Au moins en faisant correspondre les couleurs et en recadrant le texte.



Mais d'un autre côté, déjà dans le processus de développement sans la participation d'un testeur, des cas apparaissent, ce qui réduit considérablement la boucle de rétroaction.



image



désavantages



Naturellement, une telle génération de cas est loin d'être une solution miracle et a ses inconvénients.



Difficulté à analyser le résultat



Je pense que vous avez remarqué que dans le processus de génération de cas, les données de test sont mélangées les unes aux autres. Pour cette raison, lorsque le boîtier tombe, il devient plus difficile d'identifier la cause de la chute. Après tout, certains des paramètres utilisés dans le cas n'affectent en aucune façon le résultat du test.



Cela rend vraiment difficile l'analyse des résultats des tests, d'une part. Mais, d'un autre côté, si l'objet testé nécessite une grande quantité de paramètres requis, cela rend également difficile de trouver la cause du bogue.



Les bogues peuvent être manqués



Revenons au tout début de l'article: en utilisant cette méthode, nous permettons la possibilité de sauter les bogues causés par une combinaison de deux ou plusieurs paramètres. Mais nous gagnons en vitesse, c'est donc à vous de décider ce qui est le plus important pour chaque projet spécifique.



Afin de ne pas manquer deux fois les bogues, nous avons introduit la politique de zéro bogue et avons commencé à fermer chaque bogue manqué avec un cas de test supplémentaire - non plus généré automatiquement, mais écrit à la main. Cela a donné d'excellents résultats: nous avons maintenant plus de 150 blocs (objets testés), plusieurs versions par jour et de 0 à 3 bogues non critiques manqués par mois.



conclusions



Si votre objet testé a une large gamme de paramètres d'entrée et que vous souhaitez essayer de réduire le nombre de cas et, par conséquent, le temps de test, je vous recommande d'essayer la méthode ci-dessus pour générer des cas en utilisant un paramètre.



À mon avis, il est idéal pour les composants frontaux: vous pouvez réduire le temps de plus de trois fois, par exemple pour vérifier l'apparence via des tests de capture d'écran. Et le développement ira plus vite en raison de l'apparition de cas aux premiers stades.



Bien sûr, si vous testez le pilote automatique de la nouvelle Tesla, même la faible probabilité de manquer un bogue ne doit pas être négligée. Mais dans la plupart des cas, n'oubliez pas que la vitesse dans le monde moderne est un critère de qualité très important. Et l'augmentation de la vitesse donne des résultats plus positifs que quelques problèmes mineurs constatés.



image



Et pour les plus responsables, dans le prochain article, je vous expliquerai comment vous pouvez en outre vous protéger des bugs délicats causés par une combinaison de paramètres utilisant des cas personnalisés et StoryBook.



All Articles