Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ

Наш підрозділ створює повністю автоматичні пайплайни для виведення нових версій додатків до прод-середовища. Зрозуміло, для цього потрібні функціональні автоматизовані тести. Під катом - історія про те, як, розпочавши з тестування в один потік на локальній машині, ми дійшли до багатопоточного запуску автотестів на Selenoid у пайплайні збірки з Allure-звітом на GitLab pages і в результаті отримали крутий інструмент для автоматизації, який зможуть використати майбутні команди.

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ

З чого ми починали

Щоб реалізувати автотести та вбудувати їх у пайплайн, нам був потрібний фреймворк для автоматизації, який можна гнучко змінювати під наші потреби. В ідеалі хотілося отримати єдиний стандарт для двигуна автотестування, пристосований під вбудовування автотестів у пайплайн. Для реалізації ми обрали такі технології:

  • Ява,
  • Мейвен,
  • селен,
  • Cucumber+JUNIT 4,
  • Allure,
  • GitLab.

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ

Чому саме такий набір? Java - одна з найпопулярніших мов для автотестів, до того ж ним володіють усі члени команди. Selenium – очевидне рішення. Cucumber також мав підвищити довіру до результатів автотестів з боку підрозділів, що займаються ручним тестуванням.

Однопотокові тести

Щоб не винаходити велосипеда, за основу фреймворку ми взяли напрацювання з різних репозиторіїв на GitHub і адаптували їх під себе. Створили репозиторій для головної бібліотеки з ядром фреймворку автотестів та репозиторій із Gold-прикладом реалізації автотестів на нашому ядрі. Кожна команда мала брати Gold-образ і розробляти у ньому тести, адаптуючи під свій проект. Розгорнули у банку GitLab-CI, на якому налаштували:

  • щоденні запуски всіх написаних автотестів за кожним проектом;
  • запуски в пайплайні збирання.

Спершу тестів було мало, і вони йшли в один потік. Однопотоковий запуск на Windows-раннері GitLab нас цілком влаштовував: тести дуже незначно навантажували тестовий стенд і майже не утилізували ресурси.

Згодом автотестів ставало дедалі більше, і ми задумалися над їхнім паралельним запуском, коли повний прогін почав займати близько трьох годин. З'явилися й інші проблеми:

  • ми не могли переконатися, що тести стабільні;
  • тести, які проходили по кілька прогонів поспіль на локальній машині, іноді падали до CI.

Приклад налаштування автотестів:

<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>

 Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ
Приклад Allure-звіту

 Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ
Навантаження на раннер під час тестів (8 ядер, 8 ГБ ОЗП, 1 потік)
 
Плюси однопотокових тестів:

  • легко налаштувати та запустити;
  • запуски у CI практично не відрізняються від локальних запусків;
  • тести не афектять одне одного;
  • мінімальні вимоги до ресурсів раннера.

Мінуси однопотокових тестів:

  • дуже довго виконуються;
  • тривала стабілізація тестів;
  • неефективне використання ресурсів раннера, вкрай низька утилізація.

Тести на форках JVM

Оскільки при реалізації базового фреймворку ми не подбали про thread-safe код, найочевиднішим способом паралельного запуску став cucumber-jvm-parallel-plugin для Maven Плагін легко налаштувати, але для коректної паралельної роботи автотести потрібно запускати в окремих браузерах. Робити нема чого, довелося використовувати Selenoid.

Selenoid-сервер підняли на машині з 32 ядрами та 24 ГБ ОЗУ. Ліміт встановили в 48 браузерів - 1,5 потоку на ядро ​​та близько 400 МБ ОЗУ. У результаті час тестів скоротився з трьох годин до 40 хвилин. Прискорення прогонів допомогло вирішити проблему стабілізації: тепер ми могли швидко проганяти нові автотести 20–30 разів, доки переконаємося, що вони виконуються стабільно.
Першим недоліком рішення стала висока утилізація ресурсів раннерів при невеликій кількості паралельних потоків: на 4 ядрах та 8 ГБ ОЗУ тести працювали стабільно не більше ніж у 6 потоків. Другий мінус: плагін генерує класи-раннери для кожного сценарію, хоч би скільки їх запускалося.

Важливо! Не прокидайте змінну з тегами в argLineнаприклад, так:

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

Якщо передавати тег таким чином, плагін генеруватиме раннери для всіх тестів, тобто намагатиметься запустити всі тести, пропускаючи їх відразу після запуску і створюючи при цьому безліч форків JVM.

Правильно змінну з тегом прокидати в теги у налаштуваннях плагіна, див. приклад нижче. В інших перевірених нами способах виникають проблеми із підключенням плагіна Allure.

Приклад часу прогону 6 коротких тестів із неправильним налаштуванням:

[INFO] Total time: 03:17 min

Приклад часу прогону тестів, якщо безпосередньо передавати тег у mvn … –Dcucumber.options:

[INFO] Total time: 44.467 s

Приклад налаштування автотестів:

<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>

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБ
Приклад Allure-звіту (найнестабільніший тест, 4 рерани)

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБНавантаження раннера під час тестів (8 ядер, 8 ГБ ОЗП, 12 потоків)
 
Плюси:

  • просте налаштування - потрібно лише додати плагін;
  • можливість одночасного виконання великої кількості тестів;
  • прискорення стабілізації тестів завдяки п.1. 

Мінуси:

  • потрібно кілька ОС/контейнерів;
  • високе споживання ресурсів за кожен форк;
  • плагін застарів і більше не підтримується. 

Як перемогти нестабільність 

Тестові стенди не ідеальні, як самі автотести. Не дивно, що у нас з'явилася кілька flacky-тестів. На допомогу прийшов maven surefire plugin, який з коробки підтримує перезапуск тестів, що впали. Потрібно оновити версію плагіна щонайменше до 2.21 і написати один рядок з кількістю перезапусків у pom-файлі або передати як аргумент для Maven.

Приклад налаштування автотестів:

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

Або під час запуску: mvn … -Dsurefire.rerunFailingTestsCount=2 …
Як варіант – встановити опції Maven для скрипта PowerShell (PS1):

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

Плюси:

  • не потрібно витрачати час на аналіз нестабільного тесту, коли він падає;
  • можна згладити проблеми стабільності тестового стенду.

Мінуси:

  • можна пропустити плаваючі дефекти;
  • час прогону зростає.

Паралельні тести з бібліотекою Cucumber 4

Кількість тестів зростала з кожним днем. Ми знову замислилися про прискорення прогонів. Крім того, хотілося вбудувати в пайплайн складання програми якомога більше тестів. Критичним фактором стала дуже довга генерація раннерів при запуску в паралель за допомогою Maven-плагіну.

На той момент уже вийшов Cucumber 4, тож ми вирішили переписати ядро ​​під цю версію. У release notes нам обіцяли паралельний запуск на рівні тредів. Теоретично це мало:

  • значно прискорити прогін автотестів з допомогою збільшення кількості потоків;
  • виключити втрату часу на генерацію раннерів кожного автотесту.

Оптимізувати фреймворк для багатопотокових автотестів виявилося не так складно. Cucumber 4 проганяє кожен окремий тест у виділеному потоці від початку і до кінця, тому деякі загальні static-речі були просто перетворені на ThreadLocal-змінні. 
Головне при конвертації засобами рефакторингу Idea — перевірити місця, де відбувалося порівняння змінної (наприклад, перевірка на null). Крім того, потрібно винести Allure-плагін в інструкції класу Junit Runner.

Приклад налаштування автотестів:

 
<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>

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБПриклад Allure-звіту (найнестабільніший тест, 5 реранів)

Впроваджуй, масштабуй: досвід застосування автотестів у ВТБНавантаження раннера під час тестів (8 ядер, 8 ГБ ОЗУ, 24 потоки)

Плюси:

  • низьке споживання ресурсів;
  • нативна підтримка Cucumber — не потрібні додаткові інструменти;
  • можливість запуску понад 6 потоків на ядро ​​процесора.

Мінуси:

  • потрібно стежити, щоб код підтримував багатопоточне виконання;
  • збільшується поріг входження.

Allure-звіти в GitLab pages

Після впровадження багатопотокового запуску ми почали витрачати на аналіз звітів набагато більше часу. На той момент нам доводилося викладати кожен звіт як артефакт у GitLab, потім завантажувати його, розпаковувати. Це не дуже зручно та довго. А якщо хтось ще хоче подивитися звіт у себе, то йому потрібно буде виконати ті самі операції. Нам хотілося отримувати Фідбек швидше, і вихід знайшовся - GitLab pages. Ця вбудована функція, яка доступна з коробки у всіх останніх версіях GitLab. Дозволяє деплоїти статичні сайти у себе на сервері та отримувати до них доступ за прямим посиланням.

Усі скріншоти з Allure-звітами зроблено у GitLab pages. Скрипт для деплою звіту на GitLab pages — Windows PowerShell (перед цим необхідно виконати автотести):

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

Що в підсумку 

Отже, якщо ви думали про те, чи потрібен вам Thread safe-код у Cucumber-фреймворку автотестів, тепер відповідь очевидна — з Cucumber 4 її просто впровадити, значно збільшивши тим самим кількість потоків, що запускаються одночасно. При такому способі запуску тестів питання вже стоїть про продуктивність машини з Selenoid і тестового стенду.

Практика показала, що запуск автотестів на тредах дозволяє звести витрату ресурсів до мінімуму при найкращій продуктивності. Як видно з графіків, збільшення потоків у 2 рази не призводить до аналогічного прискорення проходження тестів продуктивності. Тим не менш, ми змогли додати до складання програми більше 200 автоматичних тестів, які навіть з 5 реранами виконуються приблизно за 24 хвилини. Це дозволяє отримувати від них швидкий фідбек, а при необхідності вносити правки і повторювати процедуру знову.

Джерело: habr.com

Додати коментар або відгук