Un jour dans la vie de l'automatisation QA

Comment j'ai dû devenir un peu de qa-automation: ce que j'ai ressenti, survécu et la merveilleuse architecture et infrastructure des tests automatiques à travers les yeux d'un développeur de passage.


Présentation


Comme on dit, je ne suis pas gynécologue, je suis juste passé et j'ai décidé de regarder. Par conséquent, pour commencer, je vais dire sur les raisons de l'écriture. Il me semble qu'un spécialiste ferait face au problème un ordre de grandeur plus rapidement et ne collecterait pas autant de râteau sur son chemin. Mais il ne serait pas aussi intéressé que moi, et vous n'auriez rien à lire.


Une autre raison qui m'a amené à écrire: pendant un bon moment je n'ai pas compris ce qui était si spécial dans l'infrastructure des autotests. De plus, de nombreux managers de PM et au-dessus ne comprenaient pas pleinement ce qui est si cosmique là-bas et pourquoi les développeurs écrivent des tests unitaires pour un ou deux eux-mêmes, et pour les phycestests, des gars individuels sont pris, qui écrivent des tests plus lentement, réparent constamment quelque chose et la chance de réussir toutes les spécifications financières est strictement inférieure à 100%, à condition que l'échantillon soit suffisamment grand.


Le problème


Le troisième jour, la spécification sur le projet ferroviaire est tombée. Quelque part une semaine auparavant, notre seul ingénieur en automatisation a décidé d'aller travailler à Chicago et nous n'avons pas encore trouvé de nouveau spécialiste. Par conséquent, j'ai dû retrousser mes manches et faire semblant d'être QA. À propos de comment c'était et essayez de le dire.


Le problème semble assez inoffensif. Mais, pour commencer, un peu d'histoire et de description de l'environnement. Nous avons de nombreux sélecteurs d'adresses dans notre plateforme. Honnêtement, c'est l'une des principales entités de la plateforme. Les sélecteurs vont à Google api pour les données. Dans les autotests, toutes les demandes sont bloquées afin d'économiser de l'argent et d'accélérer les tests. De plus, un peu de logique a été ajoutée pour donner approximativement la même ligne d'adresse qui a été demandée sans passer par des services externes.


Ce qui a cassé: on entre l'adresse souhaitée dans la barre d'adresse, une liste déroulante apparaît avec plusieurs options, on sélectionne celle désirée et ... la valeur de l'élément voisin est insérée dans l'entrée. Toujours.


Un long chemin vers la vérité


Premières hypothèses et approche naïve


Sans plus tarder, j'ai fait le test de chute le plus proche, trouvé la ligne sur laquelle l'adresse a été sélectionnée et j'ai commencé à l'examiner attentivement ainsi que les voisins. La ligne semble inoffensive: new_order_page.destination_address.select(baker_street) . Mais nous comprenons que derrière un beau code, il y a toujours un tas de petits dessins étranges et des entrailles désagréables.


J'ai rapidement rappelé que toute cette économie fonctionne sur SitePrism . Cette chose permet d'envelopper la page et les éléments sur elle dans les méthodes class et class, respectivement. Pour les clics et autres actions, Capybara et RSpec sont responsables. Mais ils n'ont aucun doute, ils sont fiables, comme toute la flotte civile. Et si c'est le cas, la première hypothèse se suggère immédiatement: soit quelqu'un a mal écrit les sélecteurs pour le prisme, soit quelqu'un a tordu la disposition à l'avant.


La première partie de l'hypothèse a rapidement disparu, les sélecteurs sont parfaitement écrits. Aucun xpath avec le choix du troisième li à l'intérieur de l'élément n'y a été trouvé et le code lui-même n'a pas changé l'année dernière.


Cependant, dans le domaine de la méthode de select , une logique accumulée avec des expressions régulières pour sélectionner l'option souhaitée dans la liste déroulante. Bien sûr, je me fâche contre les regexps et je vais les vérifier. Je passe une demi-heure et je comprends que tout fonctionne bien. Exactement, la ligne nécessaire est sélectionnée. click est appelé dessus. Et tout devrait fonctionner. Autrement dit, la deuxième partie de l'hypothèse de disposition disparaît également. Mais la pensée apparaît sur la courbe js . Après tout, l'élément sur la page que nous avons est personnalisé, les js autour de lui dans l'ordre et, de plus, dans ce js venons de le bricoler récemment.


Js est à blâmer


C'est la raison standard pour tous les problèmes obscurs. Quelque chose comme "aller à js , car il est déjà sous le choc." Et, il semble, dans mon cas, ne pouvait pas non plus se passer de js . En général, sans y réfléchir à deux fois, je cours vers l'équipe frontale et pointe du doigt les tests de chute, déclarant "tout fonctionne de notre côté, veuillez réparer votre côté."


Mais, les gars du dandy ne sont pas un raté, ils font le tour pendant quelques heures, trouvent quelques bugs qui ne sont pas liés au récit et disent que js pas à blâmer! Dans le même temps, ils lèvent des informations intéressantes selon lesquelles une demande de sélection n'est pas suffisante et il en fait une autre, dont la réponse correspond exactement au contenu incorrect de l'entrée.


Je ne m'attendais pas à un tel virage. Nous devons revenir en arrière et comprendre comment la maquette des demandes fonctionne pour nous.


Approche globale


Donc, il n'y a nulle part où attendre de l'aide, Moscou est derrière nous et des trucs comme ça.


Tout d'abord, nous examinons les façons dont Moka demande à Google. Plus précisément, nous cherchons d'abord où cela se produit. Ce n'est pas une tâche banale. Il s'avère que nous avons un module entier MockServices::Base , qui est responsable de la simulation de différentes requêtes utilisant VCR . Il est agenouillé adroitement dans le contrôleur de base et rien qu'en raison du nom du service responsable des demandes externes, il est introuvable.


D'accord, j'ai trouvé le moki. Nous examinons maintenant leur mise en œuvre. La première demande est tout simplement mouillée: les informations des params sont prises et substituées dans le modèle avec la réponse. Juste au cas où, j'ai vérifié le contenu des params et, comme prévu, tout vient comme il se doit.


La méthode moka de la prochaine requête est plus intéressante. Une sorte de mock_data y apparaît. Ce ne sont pas des params et nous devons déterminer d'où vient cette date. Après être tombé cinq fois en profondeur, il est révélé que ces données sont extraites de RequestStore à l'aide de la clé x_mock_data . Déjà plus intéressant.


Nous revenons au test d'origine et remarquons qu'il y a une chose set_mock_header , qui, après un examen plus approfondi, ajoute des données au même RequestStore . Plus intéressant!


Quelque part en ce moment, une dissonance cognitive se produit dans la tête: d’une part, c’est la cause probable du problème, les variables globales que la requête secondaire nous donne se sont brisées. Mais il y a une nuance: le serveur pour les spécifications financières et les spécifications financières elles-mêmes sont deux processus indépendants (en fait, le serveur est au moins 3 processus), par conséquent, un débit avec crédit ne peut en aucun cas converger, car aucune variable globale entre les processus n'a encore été introduite dans ce monde. Et avec un serveur Web multi-thread, ce sera un jeu féroce, qui ne fonctionnera pas physiquement. Signifie que j'ai creusé quelque chose et qu'il faut chercher.


Nous regardons plus loin et trouvons un certain bm , auquel les en-têtes reposent. Allez-y et réalisez que bm est BrowserMob . Ensuite, je me suis un peu trompé, car c'est un proxy sur Java dans un wrapper ruby. Juste un piano dans les buissons.


Nous commençons à choisir plus loin et comprenons que pour les variables "globales" entre le client avec rspec et le serveur avec l'application (par exemple, puma ), les mêmes en X-Mock-Data têtes X-Mock-Data dans la demande sont utilisés. Le problème est que l'application ne doit rien savoir de ces lecteurs. Juste pour cela, vous avez besoin d'un proxy à travers lequel toutes les demandes voleront et qui se chargera de configurer les en-têtes. Astucieusement, vous ne direz rien.


Nous allons tester et découvre que cette chose ne fonctionne pas. Les en-têtes sont introuvables: ni dans les demandes ni dans les réponses. Mais le RequestStore est rspec côté rspec et vide du côté du serveur Web. Cela signifie à coup sûr - c'est dans le proxy.


Ensuite, entre les deux, il s'avère que nous avons non seulement des tests avec des adresses qui set_mock_header , mais aussi tout ce qui utilise le set_mock_header ci-dessus.


Super. Reste à comprendre comment y remédier.


Nous traitons avec un proxy


Nous omettons les points de fouille dans la région où le fichier jar est lancé, puis le contrôlons via Ruby. Mieux vaut prêter attention à la façon dont le proxy du navigateur est spécifié. Nous utilisons Chrome et transmettons les informations de proxy dans l'un des nombreux arguments de la ligne de commande au démarrage. La fonction proxy est que nous utilisons le fichier pac que nous générons à partir du modèle afin d'empêcher le trafic des sockets Web via le proxy.


Quelque part ici, il y a un désir d'aller chercher sur Google ce qui est sur le chrome avec la configuration du proxy. Il s'avère que vous n'avez pas besoin d'aller loin et dans la version 72+, les gars ont «terminé» son travail. À cette occasion, ils ont même apporté un bug distinct . Mon commentaire préféré:


"Pouvez-vous s'il vous plaît arrêter de supprimer la fonctionnalité?"

La tristesse est qu'elle est considérée comme une caractéristique et à l'avenir, elle promet encore plus d'étain en termes de "secret".


En bref, Chrome ne prend plus en charge le protocole file: dans l'argument proxy-pac-url . Les solutions sont meilleures l'une que l'autre:


  • passez l'argument js qui lira le fichier pac et le transformera en base64: --proxy-pac-url='data:application/x-javascript-config;base64,'$(base64 -w0 /path/to/pac/script) ;
  • élevez votre serveur web en python afin de distribuer un fichier selon un protocole plus "correct", qui est supporté dans l'argument pour pac proxy;
  • désactivez NetworkService , puis le file: protocole devrait fonctionner, mais ils promettent qu'il sera "corrigé" à l'avenir également.

Les deux premières options ne m'ont certainement pas inspiré et la troisième, curieusement, m'a aidé.


Joie de courte durée


Je me réjouis qu'une connexion délicate ait été trouvée entre les listes déroulantes inactives et le chrome mis à jour, je n'étais pas content pour longtemps. Il s'avère que notre CI a mis à jour non seulement Chrome, mais aussi tous les packages adjacents, et maintenant nous avons encore plus de tests qui se Selenium::WebDriver::Error::NoSuchDriverError raison d'une erreur inconnue Selenium::WebDriver::Error::NoSuchDriverError , qui, curieusement, n'est pas liée à chromedriver , mais est liée avec une configuration chrome, des versions de bibliothèque et l'exécution de spécifications parallèles.


Mais c'est la tâche du prochain jour ouvrable ...


Regard vers l'avenir: l'argument disable-dev-shm-usage y a aidé.


Conclusions


Ne grondez pas l'automatisation. Il semble souffrir le plus de circonstances extérieures indépendantes de lui.


Il vaut mieux se faire un ami de l'ingénieur en automatisation avec les développeurs, afin qu'ils organisent leur infrastructure avec préférence et courtisanes avec des versions fixes et un environnement de test contrôlé. Pour moi, c'est mieux que de souffrir de CI propriétaire, chacun ayant ses propres béquilles et tabourets sous-marins très sophistiqués, que vous ne découvrirez qu'après une intégration étroite de votre application et des tests avec l'environnement de quelqu'un d'autre.

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


All Articles