Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB

Nasz oddział tworzy w pełni automatyczne potoki uruchamiania nowych wersji aplikacji na środowisko produkcyjne. Wymaga to oczywiście zautomatyzowanych testów funkcjonalnych. Poniżej fragmentu znajduje się historia o tym, jak zaczynając od testów jednowątkowych na maszynie lokalnej, doszliśmy do wielowątkowego autotestu działającego na Selenoid w potoku kompilacji z raportem Allure na stronach GitLab i ostatecznie otrzymaliśmy fajne narzędzie do automatyzacji aby przyszli ludzie mogli korzystać z zespołów.

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB

Gdzie zaczęliśmy?

Aby wdrożyć autotesty i zintegrować je z potokiem, potrzebowaliśmy struktury automatyzacji, którą można elastycznie zmieniać w celu dostosowania do naszych potrzeb. W idealnym przypadku chciałem uzyskać jeden standard dla silnika autotestowania, przystosowany do osadzania autotestów w potoku. Do realizacji wybraliśmy następujące technologie:

  • Jawa,
  • Mistrzu,
  • Selen,
  • Ogórek+JUNIT 4,
  • Nęcić,
  • GitLab.

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB

Dlaczego akurat ten zestaw? Java to jeden z najpopularniejszych języków testów automatycznych i wszyscy członkowie zespołu nim posługują się. Selen jest oczywistym rozwiązaniem. Ogórek miał między innymi zwiększyć zaufanie do wyników testów automatycznych ze strony działów zajmujących się testowaniem manualnym.

Testy jednowątkowe

Aby nie wymyślać koła na nowo, jako podstawę frameworka wzięliśmy rozwój z różnych repozytoriów w GitHub i zaadaptowaliśmy je dla siebie. Stworzyliśmy repozytorium dla biblioteki głównej z rdzeniem frameworka autotestów oraz repozytorium ze złotym przykładem implementacji autotestów na naszym rdzeniu. Każdy zespół musiał wziąć obraz Gold i opracować w nim testy, dostosowując go do swojego projektu. Wdrożyliśmy go w banku GitLab-CI, na którym skonfigurowaliśmy:

  • codzienne przebiegi wszystkich pisemnych autotestów dla każdego projektu;
  • uruchamia się w potoku kompilacji.

Początkowo testów było kilka i prowadzono je w jednym strumieniu. Praca jednowątkowa w środowisku GitLab Windows Runner całkiem nam odpowiadała: testy obciążały stanowisko testowe bardzo lekko i prawie nie zużywały żadnych zasobów.

Z biegiem czasu liczba autotestów stawała się coraz większa i pomyśleliśmy o przeprowadzeniu ich równolegle, gdy pełny przebieg zaczął trwać około trzech godzin. Pojawiły się także inne problemy:

  • nie mogliśmy zweryfikować, czy testy były stabilne;
  • testy uruchamiane kilka razy z rzędu na komputerze lokalnym czasami ulegały awarii w CI.

Przykład konfiguracji autotestów:

<plugins>
	
<plugin>
    	
<groupId>org.apache.maven.plugins</groupId>
    	
<artifactId>maven-surefire-plugin</artifactId>
    	
<version>2.20</version>
    	
<configuration>
        	
<skipTests>${skipTests}</skipTests>
        	
<testFailureIgnore>false</testFailureIgnore>
        	
<argLine>
            	
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
            	
-Dcucumber.options="--tags ${TAGS} --plugin io.qameta.allure.cucumber2jvm.AllureCucumber2Jvm --plugin pretty"
        	
</argLine>
    	
</configuration>
	
    <dependencies>
        	
<dependency>
            	
<groupId>org.aspectj</groupId>
            	
<artifactId>aspectjweaver</artifactId>
            	
<version>${aspectj.version}</version>
        	
</dependency>
    	
</dependencies>
	
</plugin>
	
<plugin>
    	
<groupId>io.qameta.allure</groupId>
    	
<artifactId>allure-maven</artifactId>
    	
<version>2.9</version>
	
</plugin>
</plugins>

 Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB
Przykład raportu dotyczącego uroku

 Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB
Obciążenie Runnera podczas testów (8 rdzeni, 8 GB RAM, 1 wątek)
 
Zalety testów jednowątkowych:

  • łatwy w konfiguracji i uruchomieniu;
  • premiery w CI praktycznie nie różnią się od premier lokalnych;
  • testy nie wpływają na siebie;
  • minimalne wymagania dotyczące zasobów biegacza.

Wady testów jednowątkowych:

  • ukończenie zajmuje bardzo dużo czasu;
  • długa stabilizacja testów;
  • nieefektywne wykorzystanie zasobów biegacza, wyjątkowo niskie wykorzystanie.

Testy na forkach JVM

Ponieważ podczas implementowania podstawowego frameworka nie zadbaliśmy o kod bezpieczny dla wątków, najbardziej oczywistym sposobem na równoległe działanie było wtyczka-równoległa-ogórek-jvm dla Mavena. Wtyczka jest łatwa w konfiguracji, jednak do poprawnego działania równoległego konieczne jest uruchomienie autotestów w osobnych przeglądarkach. Nie ma co robić, musiałem użyć Selenoidu.

Serwer Selenoid został uruchomiony na maszynie z 32 rdzeniami i 24 GB RAM-u. Limit ustalono na 48 przeglądarek – 1,5 wątku na rdzeń i około 400 MB RAM-u. W rezultacie czas testu został skrócony z trzech godzin do 40 minut. Przyspieszenie przebiegów pomogło rozwiązać problem ze stabilizacją: teraz mogliśmy szybko przeprowadzić nowe autotesty 20–30 razy, aż uzyskaliśmy pewność, że działają niezawodnie.
Pierwszą wadą rozwiązania było duże wykorzystanie zasobów runnera przy małej liczbie równoległych wątków: na 4 rdzeniach i 8 GB RAM-u testy przebiegały stabilnie w nie więcej niż 6 wątkach. Druga wada: wtyczka generuje klasy biegaczy dla każdego scenariusza, niezależnie od tego, ile z nich zostanie uruchomionych.

Ważne! Nie przekazuj zmiennej ze znacznikami do argLiniana przykład tak:

<argLine>-Dcucumber.options="--tags ${TAGS} --plugin io.qameta.allure.cucumber2jvm.AllureCucumber2Jvm --plugin pretty"</argLine>
…
Mvn –DTAGS="@smoke"

Jeśli przekażesz w ten sposób tag, wtyczka wygeneruje biegacze dla wszystkich testów, czyli będzie próbowała uruchomić wszystkie testy, pomijając je natychmiast po uruchomieniu i tworząc wiele forków JVM.

Poprawne jest wrzucenie zmiennej ze znacznikiem do tagi w ustawieniach wtyczki, patrz przykład poniżej. Inne testowane przez nas metody powodują problemy z podłączeniem wtyczki Allure.

Przykład czasu działania 6 krótkich testów z nieprawidłowymi ustawieniami:

[INFO] Total time: 03:17 min

Przykład czasu wykonania testu, jeśli bezpośrednio przeniesiesz tag do mvn... –Opcje.ogórka:

[INFO] Total time: 44.467 s

Przykład konfiguracji autotestów:

<profiles>
	
<profile>
    	
<id>parallel</id>
    	
<build>
        	
<plugins>
            	
<plugin>
                	
<groupId>com.github.temyers</groupId>
                	
<artifactId>cucumber-jvm-parallel-plugin</artifactId>
                	
<version>5.0.0</version>
                	
<executions>
                    	
<execution>
                        	
<id>generateRunners</id>
                        	
<phase>generate-test-sources</phase>
                        	
<goals>
                            	
<goal>generateRunners</goal>
                        	
</goals>
                        	
<configuration>
                	
            <tags>
                            	
<tag>${TAGS}</tag>
                            	
</tags>
                            	
<glue>
                                	
<package>stepdefs</package>
                            	
</glue>
                        	
</configuration>
     	
               </execution>
                	
</executions>
    	
        </plugin>
            	
<plugin>
                	
<groupId>org.apache.maven.plugins</groupId>
                	
<artifactId>maven-surefire-plugin</artifactId>
        	
        <version>2.21.0</version>
                	
<configuration>
                    	
<forkCount>12</forkCount>
                    	
<reuseForks>false</reuseForks>
                    	
<includes>**/*IT.class</includes>
                   	
 <testFailureIgnore>false</testFailureIgnore>
                    	
<!--suppress UnresolvedMavenProperty -->
                    	
<argLine>
  	
 -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Dcucumber.options="--plugin io.qameta.allure.cucumber2jvm.AllureCucumber2Jvm TagPFAllureReporter --plugin pretty"
                    	
</argLine>
                	
</configuration>
                	
<dependencies>
                    	
<dependency>
                        	
<groupId>org.aspectj</groupId>
                        	
<artifactId>aspectjweaver</artifactId>
                        	
<version>${aspectj.version}</version>
                 	
   </dependency>
                	
</dependencies>
         	
   </plugin>
        	
</plugins>
    	
</build>
	
</profile>

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTB
Przykład raportu Allure (najbardziej niestabilny test, 4 powtórki)

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTBObciążenie Runnera podczas testów (8 rdzeni, 8 GB RAM, 12 wątków)
 
Plusy:

  • łatwa konfiguracja - wystarczy dodać wtyczkę;
  • możliwość jednoczesnego wykonywania dużej liczby testów;
  • przyspieszenie stabilizacji testu dzięki krokowi 1. 

Wady:

  • Wymaganych wiele systemów operacyjnych/kontenerów;
  • wysokie zużycie zasobów na każde rozwidlenie;
  • Wtyczka jest przestarzała i nie jest już obsługiwana. 

Jak przezwyciężyć niestabilność 

Stanowiska testowe nie są idealne, podobnie jak same autotesty. Nic dziwnego, że mamy wiele niestabilnych testów. Przybył na ratunek wtyczka Maven Surefire, który od razu po wyjęciu z pudełka obsługuje ponowne uruchamianie nieudanych testów. Musisz zaktualizować wtyczkę do wersji co najmniej 2.21 i wpisać w pliku pom jedną linijkę z liczbą ponownych uruchomień lub przekazać ją jako argument do Mavena.

Przykład konfiguracji autotestów:

   	
<plugin>
        	
<groupId>org.apache.maven.plugins</groupId>
  	
      <artifactId>maven-surefire-plugin</artifactId>
        	
<version>2.21.0</version>
        	
<configuration>
           	
….
            	
<rerunFailingTestsCount>2</rerunFailingTestsCount>
            	
….
            	
</configuration>
</plugin>

Lub przy uruchomieniu: mvn … -Dsurefire.rerunFailingTestsCount=2 …
Opcjonalnie ustaw opcje Mavena dla skryptu PowerShell (PS1):

  
Set-Item Env:MAVEN_OPTS "-Dfile.encoding=UTF-8 -Dsurefire.rerunFailingTestsCount=2"

Plusy:

  • nie ma potrzeby tracić czasu na analizę niestabilnego testu w przypadku jego awarii;
  • można złagodzić problemy ze stabilnością stanowiska badawczego.

Wady:

  • można przeoczyć wady pływające;
  • czas pracy wzrasta.

Testy równoległe z biblioteką Cucumber 4

Liczba testów rosła z każdym dniem. Znowu pomyśleliśmy o przyspieszeniu biegów. Dodatkowo chciałem zintegrować jak najwięcej testów z potokiem montażu aplikacji. Krytycznym czynnikiem było to, że generowanie modułów biegaczy trwało zbyt długo, gdy działały równolegle przy użyciu wtyczki Maven.

W tym czasie wydano już Cucumber 4, więc postanowiliśmy przepisać jądro dla tej wersji. W informacjach o wydaniu obiecano nam równoległe uruchomienie na poziomie wątku. Teoretycznie powinno być tak:

  • znacznie przyspieszyć działanie autotestów poprzez zwiększenie liczby wątków;
  • wyeliminuj stratę czasu na generowanie przebiegów dla każdego autotestu.

Optymalizacja frameworka pod kątem autotestów wielowątkowych okazała się nie taka trudna. Cucumber 4 uruchamia każdy indywidualny test w dedykowanym wątku od początku do końca, więc niektóre typowe elementy statyczne zostały po prostu przekonwertowane na zmienne ThreadLocal. 
Najważniejszą rzeczą przy konwersji za pomocą narzędzi do refaktoryzacji Idea jest sprawdzenie miejsc, w których zmienna była porównywana (np. sprawdzenie, czy zmienna ma wartość null). Dodatkowo należy dodać wtyczkę Allure do adnotacji klasy Junit Runner.

Przykład konfiguracji autotestów:

 
<profile>
	
<id>parallel</id>
	
<build>
    	
<plugins>
        	
<plugin>
            	
<groupId>org.apache.maven.plugins</groupId>
 	
           <artifactId>maven-surefire-plugin</artifactId>
            	
<version>3.0.0-M3</version>
   	
         <configuration>
                	
<useFile>false</useFile>
                	
<testFailureIgnore>false</testFailureIgnore>
        	
        <parallel>methods</parallel>
                	
<threadCount>6</threadCount>
                	
<perCoreThreadCount>true</perCoreThreadCount>
                	
<argLine>
                    	
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                	
</argLine>
            	
</configuration>
            	
<dependencies>
                	
<dependency>
                    	
<groupId>org.aspectj</groupId>
   	
                 <artifactId>aspectjweaver</artifactId>
                    	
<version>${aspectj.version}</version>
                	
</dependency>
            	
</dependencies>
        	
</plugin>
    	
</plugins>
	
</build>
</profile>

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTBPrzykład raportu Allure (najbardziej niestabilny test, 5 powtórzeń)

Wdrażaj, skaluj: doświadczenie w stosowaniu testów automatycznych w VTBObciążenie Runnera podczas testów (8 rdzeni, 8 GB RAM, 24 wątki)

Plusy:

  • niskie zużycie zasobów;
  • natywne wsparcie od Cucumbera – nie są wymagane żadne dodatkowe narzędzia;
  • możliwość uruchomienia więcej niż 6 wątków na rdzeń procesora.

Wady:

  • musisz upewnić się, że kod obsługuje wykonanie wielowątkowe;
  • wzrasta próg wejścia.

Raporty Allure na stronach GitLab

Po wprowadzeniu wykonywania wielowątkowego zaczęliśmy spędzać znacznie więcej czasu na analizie raportów. W tamtym czasie musieliśmy przesłać każdy raport jako artefakt do GitLab, następnie pobrać go i rozpakować. Nie jest to zbyt wygodne i zajmuje dużo czasu. A jeśli ktoś inny chce sam obejrzeć raport, będzie musiał wykonać te same operacje. Chcieliśmy szybciej otrzymywać informację zwrotną i znaleźliśmy rozwiązanie – strony GitLab. Jest to wbudowana funkcja, która jest dostępna od razu we wszystkich najnowszych wersjach GitLaba. Umożliwia wdrażanie witryn statycznych na serwerze i uzyskiwanie do nich dostępu za pośrednictwem bezpośredniego łącza.

Wszystkie zrzuty ekranu raportów Allure zostały zrobione na stronach GitLab. Skrypt do wdrożenia raportu na stronach GitLab - w Windows PowerShell (wcześniej należy uruchomić autotesty):

New-Item -ItemType directory -Path $testresulthistory | Out-Null

try {Invoke-WebRequest -Uri $hst -OutFile $outputhst}
Catch{echo "fail copy history"}
try {Invoke-WebRequest -Uri $hsttrend -OutFile $outputhsttrnd}
Catch{echo "fail copy history trend"}

mvn allure:report
#mvn assembly:single -PzipAllureReport
xcopy $buildlocationtargetsiteallure-maven-plugin* $buildlocationpublic /s /i /Y

Z tym, że 

Jeśli więc zastanawiałeś się, czy potrzebujesz bezpiecznego kodu Thread w frameworku autotestu Cucumber, teraz odpowiedź jest oczywista - w Cucumber 4 łatwo go zaimplementować, znacznie zwiększając w ten sposób liczbę wątków uruchamianych jednocześnie. Przy tej metodzie przeprowadzania testów pojawia się teraz pytanie o wydajność maszyny z Selenoidem i stanowiskiem testowym.

Praktyka pokazała, że ​​uruchamianie autotestów na wątkach pozwala na ograniczenie zużycia zasobów do minimum przy najlepszej wydajności. Jak widać z wykresów, podwojenie wątków nie prowadzi do podobnego przyspieszenia w testach wydajnościowych. Udało nam się jednak dodać do kompilacji aplikacji ponad 2 automatycznych testów, które nawet przy 200 powtórzeniach trwają około 5 minut. Dzięki temu możesz szybko uzyskać od nich informację zwrotną i w razie potrzeby wprowadzić zmiany i powtórzyć procedurę jeszcze raz.

Źródło: www.habr.com

Dodaj komentarz