Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB

La nostra divisione crea pipeline completamente automatiche per il lancio di nuove versioni di applicazioni nell'ambiente di produzione. Naturalmente, ciò richiede test funzionali automatizzati. Sotto il taglio c'è una storia su come, iniziando con il test a thread singolo su una macchina locale, siamo arrivati ​​al punto dell'autotest multi-thread in esecuzione su Selenoid nella pipeline di compilazione con un rapporto Allure sulle pagine GitLab e alla fine abbiamo ottenuto un interessante strumento di automazione che le persone future possano usare i team.

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB

Da dove abbiamo iniziato?

Per implementare gli autotest e integrarli nella pipeline, avevamo bisogno di un framework di automazione che potesse essere modificato in modo flessibile per soddisfare le nostre esigenze. Idealmente, volevo ottenere un unico standard per il motore di autotest, adattato per incorporare gli autotest nella pipeline. Per la realizzazione abbiamo scelto le seguenti tecnologie:

  • Giava,
  • Esperto di,
  • Selenio,
  • Cetriolo+JUNIT 4,
  • fascino,
  • GitLab.

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB

Perché questo particolare set? Java è uno dei linguaggi più popolari per i test automatizzati e tutti i membri del team lo parlano. Il selenio è la soluzione ovvia. Il cetriolo, tra le altre cose, avrebbe dovuto aumentare la fiducia nei risultati dei test automatizzati da parte dei dipartimenti coinvolti nei test manuali.

Test a thread singolo

Per non reinventare la ruota, abbiamo preso gli sviluppi da diversi repository su GitHub come base per il framework e li abbiamo adattati per noi stessi. Abbiamo creato un repository per la libreria principale con il nucleo del framework di autotest e un repository con un esempio Gold di implementazione degli autotest sul nostro core. Ciascun team ha dovuto prendere l'immagine Gold e svilupparvi dei test, adattandola al proprio progetto. Lo abbiamo distribuito sulla banca GitLab-CI, sulla quale abbiamo configurato:

  • esecuzione giornaliera di tutti gli autotest scritti per ciascun progetto;
  • viene lanciato nella pipeline di compilazione.

All'inizio c'erano pochi test e venivano eseguiti in un unico flusso. L'esecuzione a thread singolo sul runner Windows GitLab ci è andata abbastanza bene: i test hanno caricato il banco di prova in modo molto leggero e non hanno utilizzato quasi nessuna risorsa.

Con il passare del tempo il numero degli autotest è diventato sempre più numeroso e abbiamo pensato di eseguirli in parallelo, quando un giro completo ha iniziato a durare circa tre ore. Sono comparsi anche altri problemi:

  • non abbiamo potuto verificare che i test fossero stabili;
  • i test eseguiti più volte di seguito sul computer locale a volte si bloccavano in CI.

Esempio di impostazione degli autotest:

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

 Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB
Esempio di rapporto Allure

 Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB
Carico del runner durante i test (8 core, 8 GB di RAM, 1 thread)
 
Pro dei test a thread singolo:

  • facile da configurare ed eseguire;
  • i lanci in CI non sono praticamente diversi dai lanci locali;
  • i test non si influenzano a vicenda;
  • requisiti minimi per le risorse dei corridori.

Svantaggi dei test a thread singolo:

  • richiedere molto tempo per essere completato;
  • lunga stabilizzazione dei test;
  • uso inefficiente delle risorse del corridore, utilizzo estremamente basso.

Test sui fork JVM

Dato che non ci siamo presi cura del codice thread-safe durante l'implementazione del framework di base, il modo più ovvio per eseguire in parallelo è stato cetriolo-jvm-plug-parallelo per Maven. Il plugin è facile da configurare, ma per un corretto funzionamento parallelo gli autotest devono essere eseguiti in browser separati. Non c'è niente da fare, ho dovuto usare Selenoid.

Il server Selenoid è stato lanciato su una macchina con 32 core e 24 GB di RAM. Il limite è stato fissato a 48 browser: 1,5 thread per core e circa 400 MB di RAM. Di conseguenza, il tempo del test è stato ridotto da tre ore a 40 minuti. Accelerare le esecuzioni ha aiutato a risolvere il problema della stabilizzazione: ora potevamo eseguire rapidamente nuovi autotest 20-30 volte finché non eravamo sicuri che funzionassero in modo affidabile.
Il primo inconveniente della soluzione è stato l'elevato utilizzo delle risorse del runner con un numero limitato di thread paralleli: su 4 core e 8 GB di RAM, i test sono stati eseguiti stabilmente in non più di 6 thread. Il secondo svantaggio: il plugin genera classi runner per ogni scenario, indipendentemente da quante ne vengano avviate.

Importante! Non passare una variabile con tag a argLine, ad esempio, in questo modo:

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

Se passi il tag in questo modo, il plugin genererà dei runner per tutti i test, cioè proverà a eseguire tutti i test, saltandoli subito dopo il lancio e creando molti fork JVM.

È corretto inserire una variabile con un tag tag nelle impostazioni del plugin, vedere l'esempio seguente. Altri metodi che abbiamo testato presentano problemi di connessione al plug-in Allure.

Esempio di tempo di esecuzione per 6 test brevi con impostazioni errate:

[INFO] Total time: 03:17 min

Esempio di esecuzione del test se si trasferisce direttamente il tag a mvn... –Dcucumber.opzioni:

[INFO] Total time: 44.467 s

Esempio di impostazione degli autotest:

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

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTB
Esempio di report Allure (il test più instabile, 4 repliche)

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTBCarico del runner durante i test (8 core, 8 GB di RAM, 12 thread)
 
pro:

  • configurazione semplice: devi solo aggiungere un plugin;
  • la capacità di eseguire contemporaneamente un gran numero di test;
  • accelerazione della stabilizzazione del test grazie allo step 1. 

contro:

  • Sono necessari più sistemi operativi/contenitori;
  • elevato consumo di risorse per ogni fork;
  • Il plugin è obsoleto e non è più supportato. 

Come superare l'instabilità 

I banchi prova non sono ideali, proprio come gli stessi autotest. Non sorprende che abbiamo una serie di test inconsistenti. È venuto in soccorso plugin maven surefire, che supporta immediatamente il riavvio dei test non riusciti. È necessario aggiornare la versione del plugin almeno alla 2.21 e scrivere una riga con il numero di riavvii nel file pom o passarla come argomento a Maven.

Esempio di impostazione degli autotest:

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

Oppure all'avvio: mvn... -Dsurefire.rerunFailingTestsCount=2...
Come opzione, imposta le opzioni Maven per lo script PowerShell (PS1):

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

pro:

  • non c'è bisogno di perdere tempo analizzando un test instabile quando si blocca;
  • i problemi di stabilità del banco prova possono essere mitigati.

contro:

  • i difetti fluttuanti possono essere persi;
  • il tempo di esecuzione aumenta.

Test paralleli con la libreria Cucumber 4

Il numero di test cresceva ogni giorno. Abbiamo nuovamente pensato di accelerare le corse. Inoltre, volevo integrare il maggior numero possibile di test nella pipeline di assemblaggio dell'applicazione. Il fattore critico era che la generazione dei corridori impiegava troppo tempo quando si correva in parallelo utilizzando il plugin Maven.

A quel tempo Cucumber 4 era già stato rilasciato, quindi abbiamo deciso di riscrivere il kernel per questa versione. Nelle note di rilascio ci era stato promesso un lancio parallelo a livello di thread. Teoricamente questo avrebbe dovuto essere:

  • velocizzare significativamente l'esecuzione degli autotest aumentando il numero di thread;
  • eliminare la perdita di tempo nella generazione dei corridori per ogni autotest.

L'ottimizzazione del framework per gli autotest multi-thread non si è rivelata così difficile. Cucumber 4 esegue ogni singolo test su un thread dedicato dall'inizio alla fine, quindi alcune cose statiche comuni sono state semplicemente convertite in variabili ThreadLocal. 
La cosa principale quando si esegue la conversione utilizzando gli strumenti di refactoring di Idea è controllare i punti in cui è stata confrontata la variabile (ad esempio, controllando se è nullo). Inoltre, è necessario aggiungere il plugin Allure all'annotazione della classe Junit Runner.

Esempio di impostazione degli autotest:

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

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTBEsempio di report Allure (il test più instabile, 5 repliche)

Implementare, scalare: esperienza nell'uso di test automatizzati presso VTBCarico del runner durante i test (8 core, 8 GB di RAM, 24 thread)

pro:

  • basso consumo di risorse;
  • supporto nativo di Cucumber: non sono richiesti strumenti aggiuntivi;
  • la capacità di eseguire più di 6 thread per core del processore.

contro:

  • è necessario assicurarsi che il codice supporti l'esecuzione multi-thread;
  • la soglia di ingresso aumenta.

Report Allure sulle pagine GitLab

Dopo aver introdotto l'esecuzione multi-thread, abbiamo iniziato a dedicare molto più tempo all'analisi dei report. A quel tempo, dovevamo caricare ogni report come artefatto su GitLab, quindi scaricarlo e decomprimerlo. Non è molto conveniente e richiede molto tempo. E se qualcun altro desidera visualizzare personalmente il rapporto, dovrà eseguire le stesse operazioni. Volevamo ricevere feedback più velocemente e abbiamo trovato una soluzione: le pagine GitLab. Questa è una funzionalità integrata disponibile immediatamente in tutte le versioni recenti di GitLab. Ti consente di distribuire siti statici sul tuo server e accedervi tramite un collegamento diretto.

Tutti gli screenshot dei report Allure sono stati acquisiti sulle pagine GitLab. Script per la distribuzione del report nelle pagine GitLab - in Windows PowerShell (prima è necessario eseguire gli autotest):

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

Con il risultato che 

Quindi, se stavi pensando se hai bisogno del codice Thread-safe nel framework di autotest di Cucumber, ora la risposta è ovvia: con Cucumber 4 è facile implementarlo, aumentando così in modo significativo il numero di thread avviati contemporaneamente. Con questo metodo di esecuzione dei test, la questione ora diventa la prestazione della macchina con Selenoid e il banco di prova.

La pratica ha dimostrato che l'esecuzione di test automatici sui thread consente di ridurre al minimo il consumo di risorse con le migliori prestazioni. Come si può vedere dai grafici, il raddoppio dei thread non porta ad una simile accelerazione nei test prestazionali. Tuttavia, siamo riusciti ad aggiungere più di 2 test automatizzati alla build dell'applicazione, che anche con 200 ripetizioni vengono eseguiti in circa 5 minuti. Ciò ti consente di ricevere un rapido feedback da loro e, se necessario, apportare modifiche e ripetere nuovamente la procedura.

Fonte: habr.com

Aggiungi un commento