Implementar, dimensionar: experiência de uso de testes automatizados em VTB

Nossa divisão cria pipelines totalmente automáticos para lançar novas versões de aplicativos no ambiente de produção. Claro, isso requer testes funcionais automatizados. Abaixo do corte está uma história sobre como, começando com testes de thread único em uma máquina local, chegamos ao ponto de autoteste multithread em execução no Selenoid no pipeline de construção com um relatório Allure nas páginas do GitLab e, eventualmente, obtivemos uma ferramenta de automação interessante que as futuras pessoas possam usar equipes.

Implementar, dimensionar: experiência de uso de testes automatizados em VTB

Por onde começamos?

Para implementar autotestes e integrá-los ao pipeline, precisávamos de uma estrutura de automação que pudesse ser alterada de forma flexível para atender às nossas necessidades. Idealmente, eu queria obter um padrão único para o mecanismo de autoteste, adaptado para incorporar autotestes no pipeline. Para implementação escolhemos as seguintes tecnologias:

  • Java
  • Especialista,
  • Selênio,
  • Pepino+JUNITO 4,
  • Fascínio,
  • GitLab.

Implementar, dimensionar: experiência de uso de testes automatizados em VTB

Por que este conjunto específico? Java é uma das linguagens mais populares para testes automatizados e todos os membros da equipe a falam. O selênio é a solução óbvia. O pepino, entre outras coisas, deveria aumentar a confiança nos resultados de testes automatizados por parte dos departamentos envolvidos em testes manuais.

Testes de thread único

Para não reinventar a roda, pegamos desenvolvimentos de vários repositórios no GitHub como base para o framework e os adaptamos para nós mesmos. Criamos um repositório para a biblioteca principal com o núcleo do framework de autoteste e um repositório com um exemplo Gold de implementação de autotestes em nosso núcleo. Cada equipe teve que pegar a imagem Gold e desenvolver testes nela, adaptando-a ao seu projeto. Implantamos no banco GitLab-CI, no qual configuramos:

  • execuções diárias de todos os autotestes escritos para cada projeto;
  • é lançado no pipeline de construção.

No início, foram poucos os testes e eles foram realizados em um único fluxo. A execução de thread único no executor GitLab do Windows nos agradou muito bem: os testes carregaram o banco de testes muito levemente e quase não usaram recursos.

Com o tempo, o número de autotestes tornou-se cada vez mais numeroso e pensamos em executá-los em paralelo, quando uma execução completa começou a demorar cerca de três horas. Outros problemas também apareceram:

  • não conseguimos verificar se os testes estavam estáveis;
  • testes executados várias vezes seguidas na máquina local às vezes travavam no CI.

Exemplo de configuração de autotestes:

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

 Implementar, dimensionar: experiência de uso de testes automatizados em VTB
Exemplo de relatório Allure

 Implementar, dimensionar: experiência de uso de testes automatizados em VTB
Carga do corredor durante os testes (8 núcleos, 8 GB de RAM, 1 thread)
 
Prós dos testes de thread único:

  • fácil de configurar e executar;
  • os lançamentos em CI praticamente não diferem dos lançamentos locais;
  • os testes não afetam uns aos outros;
  • requisitos mínimos para recursos do executor.

Desvantagens dos testes de thread único:

  • demora muito para ser concluído;
  • longa estabilização de testes;
  • uso ineficiente dos recursos do corredor, utilização extremamente baixa.

Testes em garfos JVM

Como não cuidamos do código thread-safe ao implementar a estrutura base, a maneira mais óbvia de executar em paralelo era pepino-jvm-plugin paralelo para Maven. O plugin é fácil de configurar, mas para uma operação paralela correta, os autotestes devem ser executados em navegadores separados. Não há nada a fazer, tive que usar o Selenoid.

O servidor Selenoid foi lançado em uma máquina com 32 núcleos e 24 GB de RAM. O limite foi definido em 48 navegadores – 1,5 threads por núcleo e cerca de 400 MB de RAM. Como resultado, o tempo de teste foi reduzido de três horas para 40 minutos. Acelerar as execuções ajudou a resolver o problema de estabilização: agora podíamos executar rapidamente novos autotestes 20 a 30 vezes até termos certeza de que eles funcionavam de maneira confiável.
A primeira desvantagem da solução foi a alta utilização de recursos do executor com um pequeno número de threads paralelos: em 4 núcleos e 8 GB de RAM, os testes rodaram de forma estável em no máximo 6 threads. A segunda desvantagem: o plugin gera classes de runner para cada cenário, não importa quantas delas sejam iniciadas.

Importante! Não passe uma variável com tags para argLinha, por exemplo, assim:

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

Se você passar a tag desta forma, o plugin irá gerar executores para todos os testes, ou seja, tentará executar todos os testes, ignorando-os imediatamente após o lançamento e criando vários forks JVM.

É correto lançar uma variável com uma tag em Tag nas configurações do plugin, veja o exemplo abaixo. Outros métodos que testamos apresentam problemas para conectar o plugin Allure.

Exemplo de tempo de execução para 6 testes curtos com configurações incorretas:

[INFO] Total time: 03:17 min

Exemplo de tempo de execução de teste se você transferir diretamente a tag para mvn... –Dcucumber.options:

[INFO] Total time: 44.467 s

Exemplo de configuração de autotestes:

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

Implementar, dimensionar: experiência de uso de testes automatizados em VTB
Exemplo de relatório Allure (o teste mais instável, 4 repetições)

Implementar, dimensionar: experiência de uso de testes automatizados em VTBCarga do corredor durante os testes (8 núcleos, 8 GB de RAM, 12 threads)
 
Prós:

  • configuração fácil - você só precisa adicionar um plugin;
  • a capacidade de realizar simultaneamente um grande número de testes;
  • aceleração da estabilização do teste graças ao passo 1. 

Contras:

  • São necessários vários sistemas operacionais/contêineres;
  • alto consumo de recursos para cada bifurcação;
  • O plugin está desatualizado e não é mais compatível. 

Como superar a instabilidade 

Bancadas de testes não são ideais, assim como os próprios autotestes. Não é surpreendente que tenhamos uma série de testes instáveis. Veio para o resgate plugin infalível maven, que oferece suporte imediato à reinicialização de testes com falha. Você precisa atualizar a versão do plugin para pelo menos 2.21 e escrever uma linha com o número de reinicializações no arquivo pom ou passá-la como argumento para o Maven.

Exemplo de configuração de autotestes:

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

Ou na inicialização: mvn… -Dsurefire.rerunFailingTestsCount=2…
Como opção, defina as opções do Maven para o script do PowerShell (PS1):

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

Prós:

  • não há necessidade de perder tempo analisando um teste instável quando ele trava;
  • os problemas de estabilidade da bancada de testes podem ser mitigados.

Contras:

  • defeitos flutuantes podem passar despercebidos;
  • o tempo de execução aumenta.

Testes paralelos com a biblioteca Cucumber 4

O número de testes crescia a cada dia. Pensamos novamente em acelerar as corridas. Além disso, eu queria integrar o máximo possível de testes ao pipeline de montagem do aplicativo. O fator crítico foi que a geração de executores demorava muito ao executar em paralelo usando o plugin Maven.

Naquela época, o Cucumber 4 já havia sido lançado, então decidimos reescrever o kernel para esta versão. Nas notas de lançamento, foi prometido um lançamento paralelo no nível do thread. Teoricamente deveria ter sido:

  • acelerar significativamente a execução de autotestes, aumentando o número de threads;
  • elimine a perda de tempo na geração de corredores para cada autoteste.

Otimizar a estrutura para autotestes multithread não foi tão difícil. O Cucumber 4 executa cada teste individual em um thread dedicado do início ao fim, portanto, algumas coisas estáticas comuns foram simplesmente convertidas em variáveis ​​ThreadLocal. 
O principal ao converter usando as ferramentas de refatoração do Idea é verificar os locais onde a variável foi comparada (por exemplo, verificar se há nulo). Além disso, você precisa adicionar o plugin Allure à anotação da classe Junit Runner.

Exemplo de configuração de autotestes:

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

Implementar, dimensionar: experiência de uso de testes automatizados em VTBExemplo de relatório Allure (o teste mais instável, 5 repetições)

Implementar, dimensionar: experiência de uso de testes automatizados em VTBCarga do corredor durante os testes (8 núcleos, 8 GB de RAM, 24 threads)

Prós:

  • baixo consumo de recursos;
  • suporte nativo do Cucumber - não são necessárias ferramentas adicionais;
  • a capacidade de executar mais de 6 threads por núcleo do processador.

Contras:

  • você precisa garantir que o código suporte a execução multithread;
  • o limite de entrada aumenta.

Relatórios Allure nas páginas do GitLab

Após a introdução da execução multithread, começamos a gastar muito mais tempo analisando relatórios. Naquela época, tínhamos que fazer upload de cada relatório como um artefato para o GitLab, baixá-lo e descompactá-lo. Não é muito conveniente e leva muito tempo. E se outra pessoa quiser visualizar o relatório por si mesma, precisará fazer as mesmas operações. Queríamos receber feedback mais rapidamente e encontramos uma solução - páginas do GitLab. Este é um recurso integrado que está disponível imediatamente em todas as versões recentes do GitLab. Permite implantar sites estáticos em seu servidor e acessá-los por meio de um link direto.

Todas as capturas de tela dos relatórios do Allure foram tiradas nas páginas do GitLab. Script para implantar o relatório nas páginas do GitLab - no Windows PowerShell (antes disso você precisa executar autotestes):

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

Com o resultado de que 

Então, se você está pensando se precisa de código Thread Safe na estrutura de autoteste do Cucumber, agora a resposta é óbvia - com o Cucumber 4 é fácil implementá-lo, aumentando significativamente o número de threads iniciados simultaneamente. Com este método de execução de testes, a questão agora passa a ser sobre o desempenho da máquina com o Selenoid e a bancada de testes.

A prática tem mostrado que a execução de autotestes em threads permite reduzir ao mínimo o consumo de recursos com o melhor desempenho. Como pode ser visto nos gráficos, a duplicação de threads não leva a uma aceleração semelhante nos testes de desempenho. Porém, conseguimos adicionar mais de 2 testes automatizados à construção da aplicação, que mesmo com 200 repetições são executados em cerca de 5 minutos. Isso permite que você receba feedback rápido deles e, se necessário, faça alterações e repita o procedimento novamente.

Fonte: habr.com

Adicionar um comentário