Nous couvrons les tests A / B avec des tests UI. Comment ne pas se perdre dans le code natif

Bonjour, Habr!

Je m'appelle Vitaliy Kotov, je travaille pour Badoo et la plupart du temps je m'occupe des tests d'automatisation. Je veux partager la solution à l'une de ces questions dans cet article.

Il s'agira de la façon dont nous avons organisé le processus de travail des tests d'interface avec les tests A / B, dont nous avons beaucoup. Je parlerai des problèmes que nous avons rencontrés et des inondations auxquelles nous sommes finalement parvenus. Bienvenue au chat!



Jusqu'à ce que nous commencions ...


Le mot test est très courant dans cet article. C'est parce que nous parlons de tests d'interface utilisateur et de tests A / B en même temps. J'ai toujours essayé de séparer ces deux concepts et de formuler des réflexions pour que le texte soit facile à lire. Si quelque part, j'ai néanmoins raté la première partie du mot et écrit simplement «test», je voulais dire le test de l'interface utilisateur.

Bonne lecture!

Que sont les tests A / B


Donc, tout d'abord, définissons le concept de test A / B. Voici une citation de Wikipedia:

"Les tests A / B (tests A / B, tests fractionnés) sont une méthode de recherche marketing, dont l'essence est que le groupe de contrôle des éléments est comparé à un ensemble de groupes de tests dans lesquels un ou plusieurs indicateurs ont été modifiés, afin de pour savoir lequel des changements améliore la cible » Lien .

En termes de notre projet, la présence d'un test A / B implique que certaines fonctionnalités sont différentes pour différents utilisateurs. Je voudrais souligner plusieurs options:

  • La fonctionnalité est disponible pour un groupe d'utilisateurs, mais pas pour un autre;
  • La fonctionnalité est disponible pour tous les utilisateurs, mais elle fonctionne de différentes manières;
  • La fonctionnalité est disponible pour tous les utilisateurs, elle fonctionne de la même façon, mais elle est différente;
  • toute combinaison des trois options précédentes.

Pour que toute cette logique fonctionne, nous avons un outil dans notre société appelé UserSplit Tool , et notre développeur Rinat Akhmadeev en a parlé en détail dans cet article .

Nous allons maintenant parler de ce que signifie avoir des tests A / B pour le département de test et pour l'automatisation en particulier.

Couverture du test de l'interface utilisateur


Lorsque nous parlons de couverture de l'interface utilisateur, nous ne parlons pas du nombre de lignes de code que nous avons testées. Cela est compréhensible, car même l'ouverture d'une page peut impliquer de nombreux composants, alors que nous n'avons encore rien testé.

Au fil des années de travail dans le domaine de l'automatisation des tests, j'ai vu de nombreuses façons de mesurer la couverture des tests d'interface utilisateur. Je ne les énumérerai pas tous, je dirai simplement que nous préférons évaluer cet indicateur par le nombre de fonctionnalités couvertes par les tests d’interface utilisateur. Ce n'est pas un moyen idéal (personnellement, je ne connais pas le moyen idéal), mais dans notre cas, cela fonctionne.

Et ici, nous revenons directement au sujet de l'article. Comment mesurer et maintenir un bon niveau de couverture des tests d'interface utilisateur, lorsque chaque fonctionnalité peut se comporter différemment selon l'utilisateur qui l'utilise?

Comment les fonctionnalités ont été couvertes par les tests d'interface utilisateur initialement


Avant même que l'outil UserSplit apparaisse dans l'entreprise et qu'il y ait vraiment eu de nombreux tests A / B, nous avons adhéré à la stratégie suivante pour couvrir les fonctionnalités avec des tests d'interface utilisateur: ne couvrir que les fonctionnalités qui étaient en production depuis un certain temps et qui s'étaient installées.

Et tout cela parce que plus tôt, lorsque la fonctionnalité n'était entrée en production, elle était toujours «réglée» pendant un certain temps - son comportement et son apparence pouvaient changer. Et elle n'a pas pu non plus faire ses preuves et disparaître assez rapidement des yeux des utilisateurs. L'écriture de tests d'interface utilisateur pour des fonctionnalités instables coûte cher et n'a pas été pratiquée avec nous.

Avec l'introduction des tests A / B dans le processus de développement, rien n'a changé au début. Chaque test A / B avait un soi-disant «groupe de contrôle», c'est-à-dire un groupe qui voyait un comportement par défaut de la fonctionnalité. C'est sur lui que les tests d'interface ont été écrits. Tout ce qui devait être fait lors de l'écriture de tests d'interface utilisateur pour une telle fonctionnalité était de ne pas oublier d'activer l'utilisateur avec un comportement par défaut. Nous appelons ce processus la force du groupe A / B (de la force anglaise).

Je m'attarderai sur la description de la force plus en détail, car elle jouera toujours un rôle dans mon histoire.

Force pour tests A / B et QaAPI


Nous avons parlé à plusieurs reprises de QaAPI dans nos articles et rapports. Néanmoins, curieusement, jusqu'à présent, nous n'avons pas écrit un article complet sur cet outil. Un jour, cet écart sera probablement comblé. En attendant, vous pouvez regarder une vidéo du discours de mon collègue Dmitry Marushchenko: " 4. Le concept QaAPI: un regard sur les tests de l'autre côté des barricades ."

En un mot, QaAPI vous permet de faire des requêtes du test au serveur d'applications via une porte dérobée spéciale afin de manipuler toutes les données. À l'aide de cet outil, par exemple, nous préparons les utilisateurs à des cas de test spécifiques, leur envoyons des messages, téléchargeons des photos, etc.

En utilisant le même QaAPI, nous pouvons forcer le groupe de test A / B; il suffit d'indiquer le nom du test et le nom du groupe souhaité. L'appel de test ressemble à ceci:

QaApi::forceSpliTest(“Test name”, “Test group name”, {USER_ID or DEVICE_ID}); 

Le dernier paramètre que nous spécifions est user_id ou device_id, pour lequel cette force devrait commencer à fonctionner. Nous spécifions le paramètre device_id dans le cas d'un utilisateur non autorisé, car le paramètre user_id n'est pas encore là. C'est vrai, pour les pages non autorisées, nous avons également des tests A / B.

Après avoir appelé cette méthode QaAPI, un utilisateur autorisé ou le propriétaire de l'appareil est assuré de voir la version de la fonctionnalité que nous avons forgée. Ce sont les défis que nous avons écrits dans les tests d'interface utilisateur, qui couvraient les fonctionnalités qui sont sous test A / B.

Et donc nous avons vécu longtemps. Les tests d'interface utilisateur ne couvraient que les groupes de contrôle des tests A / B. Ensuite, il n'y en avait pas beaucoup et cela a fonctionné. Mais le temps a passé; le nombre de tests A / B a commencé à augmenter, et presque toutes les nouvelles fonctionnalités ont commencé à fonctionner sous les tests A / B. L'approche consistant à ne couvrir que les versions de contrôle des fonctionnalités a cessé de nous satisfaire. Et voici pourquoi ...

Pourquoi couvrir les tests A / B


Problème 1 - Couverture

Comme je l'ai écrit ci-dessus, au fil du temps, presque toutes les nouvelles fonctionnalités ont commencé à sortir sous des tests A / B. En plus du contrôle, chaque fonction a une, deux ou trois autres options. Il s'avère que pour une telle fonctionnalité, la couverture dans le meilleur des cas ne dépassera pas 50% et, dans le pire des cas, elle sera d'environ 25%. Auparavant, lorsqu'il y avait peu de telles caractéristiques, cela n'avait pas d'effet significatif sur le taux de couverture total. Maintenant - il a commencé à rendre.

Problème 2 - Tests A / B longs

Certains tests A / B prennent maintenant un certain temps. Et nous continuons à être libérés deux fois par jour (cela peut être trouvé dans l'article de notre ingénieur QA Ilya Kudinov, " Comment nous survivons 4 ans dans une condition de deux versions par jour ").

Ainsi, la probabilité de casser une version du test A / B pendant ce temps est incroyablement élevée. Et cela affectera certainement l'expérience utilisateur et annulera tout l'intérêt des tests A / B de la fonctionnalité: après tout, une fonctionnalité peut afficher de mauvais résultats sur certaines versions, non pas parce que les utilisateurs ne l'aiment pas, mais parce qu'elle ne fonctionne pas comme prévu.

Si nous voulons être sûrs du résultat des tests A / B, nous ne devons permettre à aucune version de la fonctionnalité de fonctionner différemment de celle attendue.

Le troisième problème est la pertinence des tests d'interface

Il y a une chose telle que la sortie d'un test A / B. Cela signifie que le test A / B a collecté suffisamment de statistiques et que le chef de produit est prêt à ouvrir l'option gagnante pour tous les utilisateurs. La publication du test A / B se produit de manière asynchrone avec la publication du code, car elle dépend de la configuration de la configuration et non du code.

Supposons que la variante sans contrôle gagne et devienne meilleure. Qu'adviendra-t-il des tests d'interface utilisateur qui ne l'ont couvert que? C'est vrai: ils vont casser. Mais que se passe-t-il s'ils se cassent une heure avant la sortie de la build? Pouvons-nous effectuer des tests de régression de cette version? Non. Comme vous le savez, avec des tests ratés, vous n'irez pas loin.

Par conséquent, vous devez être prêt à fermer tout test A / B à l'avance afin qu'il n'interfère pas avec les performances des tests d'interface utilisateur et, par conséquent, la prochaine version de la build.

Conclusion

La conclusion de ce qui précède est évidente: nous devons couvrir les tests A / B avec des tests UI dans leur intégralité, toutes les options. Est-ce logique? Oui! Merci à tous, divergent!

... blague! Pas si simple.

Interface pour les tests A / B


La première chose qui semblait gênante était le contrôle des tests et fonctionnalités A / B qui étaient déjà couverts et qui ne l'étaient pas encore. Historiquement, nous appelons les tests d'interface utilisateur selon le principe suivant:

  • nom de la fonction ou de la page;
  • description de cas;
  • Test

Par exemple, ChatBlockedUserTest, RegistrationViaFacebookTest, etc. Pousser ici aussi le nom du split test semblait inconfortable. Premièrement, les noms deviendraient incroyablement longs. Deuxièmement, les tests devraient être renommés à la fin du test A / B, ce qui aurait un effet néfaste sur la collecte de statistiques qui prend en compte le nom du test d'interface utilisateur.

Saisir le code pour appeler la méthode QaAPI tout le temps est toujours un plaisir.

Nous avons donc décidé de supprimer tous les appels à QaApi :: forceSplitTest () du code des tests d'interface utilisateur et de transférer les données sur les forces nécessaires à la table MySQL. Pour elle, nous avons fait une présentation de l'interface utilisateur sur Selenium Manager (j'en ai parlé ici ).

Cela ressemble à ceci:



Dans le tableau, vous pouvez indiquer pour quel test UI la force de quel test A / B et dans quel groupe nous voulons appliquer. Vous pouvez spécifier le nom du test d'interface utilisateur lui-même, la classe de test ou Tous.

De plus, nous pouvons indiquer si cette force s'applique aux utilisateurs autorisés ou non autorisés.

Ensuite, nous avons enseigné les tests d'interface au démarrage pour obtenir les données de ce tableau et forcer ceux qui sont directement liés au test en cours ou à tous (tous) les tests.

Ainsi, nous avons réussi à rassembler toutes les manipulations des tests A / B en un seul endroit. La liste des tests A / B couverts est désormais facile à consulter.

Là, nous avons créé un formulaire pour ajouter de nouveaux tests A / B:



Tout cela vous permet d'ajouter et de supprimer facilement et rapidement la force nécessaire sans créer de validation, en attendant qu'elle se décompose dans tous les clouds où les tests d'interface utilisateur sont exécutés, etc.

Architecture de test de l'interface utilisateur


La deuxième chose à laquelle nous avons décidé de prêter attention est une révision de l'approche de l'écriture des tests d'interface pour les tests A / B.

En un mot, je vais vous expliquer comment nous écrivons des tests d’interface utilisateur réguliers. L'architecture est assez simple et familière:

  • classes de test - il décrit la logique métier de la fonctionnalité couverte (en fait, ce sont les scripts de nos tests: a fait cela, vu cela);
  • Classes PageObject - toutes les interactions avec l'interface utilisateur et les localisateurs y sont décrites;
  • Classes TestCase - il existe des méthodes courantes qui ne sont pas directement liées à l'interface utilisateur, mais peuvent être utiles dans plusieurs classes de test (par exemple, interaction avec QaAPI);
  • core-classes - il y a la logique d'augmenter la session, la journalisation et d'autres choses que vous n'avez pas besoin de toucher lors de l'écriture d'un test régulier.

En général, cette architecture nous convient parfaitement. Nous savons que si l'interface utilisateur a changé, seules les classes PageObject doivent être modifiées (tandis que les tests eux-mêmes ne devraient pas être affectés). Si la logique métier d'une fonctionnalité a changé, nous changeons le scénario.

Comme je l'ai écrit dans un article précédent , tout le monde travaille avec les tests d'interface: à la fois les gars du département de test manuel et les développeurs. Plus ce processus est simple et compréhensible, plus souvent les personnes qui ne leur sont pas directement liées exécuteront des tests.

Mais, comme je l'ai écrit ci-dessus, contrairement aux fonctionnalités bien établies, les tests A / B vont ou viennent. Si nous les écrivons dans le même format que les tests d'interface utilisateur réguliers, nous devrons supprimer définitivement le code de nombreux endroits différents après l'achèvement des tests A / B. Vous comprenez, pour la refactorisation, surtout quand tout fonctionne sans, il n'est pas toujours possible d'allouer du temps.

Néanmoins, nous ne voulons pas laisser nos classes proliférer avec des méthodes et des localisateurs inutilisés, cela rendra les mêmes PageObjects difficiles à utiliser. Comment vous simplifier la vie?

Puis PhpStorm est venu à notre secours (merci aux gars de JetBrains pour l'IDE pratique), à ​​savoir cette fonctionnalité .

En bref, il permet d'utiliser des balises spéciales pour diviser le code en régions dites. Nous avons essayé - et nous l'avons aimé. Nous avons commencé à écrire des tests d'interface utilisateur temporaires pour les tests A / B actifs dans un fichier, en divisant les zones de code en régions indiquant la classe dans laquelle ce code devrait être placé à l'avenir.

En conséquence, le code de test ressemblait à ceci:



Dans chaque région, il existe un code qui appartient à une classe particulière. Certes, dans d'autres IDE, il y a quelque chose de similaire.

Ainsi, nous avons couvert toutes les variantes du test A / B avec une seule classe de test, en y plaçant à la fois les méthodes PageObject et les localisateurs. Et après son achèvement, nous avons d'abord supprimé les options perdantes de la classe, puis distribué assez facilement le code restant dans les classes souhaitées conformément à ce qui est indiqué dans la région.

Comment fermons-nous maintenant les tests A / B


Vous ne pouvez pas simplement passer et couvrir tous les tests A / B avec des tests d’interface utilisateur à la fois. D'un autre côté, une telle tâche n'existe pas. Le défi en termes d'automatisation est de ne couvrir rapidement que les tests importants et de longue durée.

Néanmoins, avant la sortie de tout test A / B, même le plus petit, je veux pouvoir exécuter tous les tests d'interface utilisateur sur la version gagnante et m'assurer que tout fonctionne comme il se doit et nous reproduisons des fonctionnalités de travail de haute qualité pour 100% des utilisateurs.

La solution mentionnée ci-dessus avec une table MySQL ne convient pas à cet effet. Le fait est que si vous y ajoutez de la force, elle commencera immédiatement à s’activer pour tous les tests d’interface utilisateur. En plus de la mise en scène (notre environnement de pré-production, où nous exécutons un ensemble complet de tests), cela affectera également les tests d'interface utilisateur lancés sur des branches de tâches individuelles. Des collègues du service des tests manuels travailleront avec les résultats de ces lancements. Et si un test A / B échoué a un bug, les tests pour leurs tâches tomberont également et les gars peuvent décider que le problème est dans leur tâche, et non dans le test A / B. Pour cette raison, les tests et les essais peuvent prendre beaucoup de temps (personne ne sera satisfait).

Jusqu'à présent, nous avons réussi avec des modifications minimes, en ajoutant la possibilité de spécifier l'environnement cible dans la table:



Cet environnement peut être modifié à la volée dans un enregistrement existant. Ainsi, nous pouvons ajouter de la force uniquement pour la mise en scène, sans affecter les résultats des tests réussis sur les tâches individuelles.

Pour résumer


Donc, avant le début de cette histoire, nos tests d'interface utilisateur ne couvraient que les principaux groupes (de contrôle) de tests A / B. Mais nous avons réalisé que nous en voulions plus et sommes arrivés à la conclusion qu'il était également nécessaire de couvrir d'autres versions des tests A / B.

En résumé:

  • nous avons créé une interface pour un contrôle pratique de la couverture des tests A / B; en conséquence, nous avons maintenant toutes les informations sur le fonctionnement des tests d'interface avec les tests A / B;
  • nous avons développé pour nous-mêmes un moyen d'écrire des tests d'interface utilisateur temporaires avec un flux simple et efficace pour leur suppression ou leur transfert dans les rangs du permanent;
  • nous avons appris à tester facilement et sans douleur des versions de tests A / B, sans interférer avec d'autres tests d'interface utilisateur en cours d'exécution et sans commits inutiles dans Git.

Tout cela a permis d'adapter l'automatisation des tests à des fonctionnalités en constante évolution, de contrôler et d'augmenter facilement le niveau de couverture et de ne pas surcharger un code hérité.

Avez-vous une expérience pour ramener à première vue une situation chaotique à un ordre contrôlé et vous simplifier la vie pour vous et vos collègues? Partagez-le dans les commentaires. :)

Merci de votre attention! Et bonne année!

Source: https://habr.com/ru/post/fr434448/


All Articles