Avaliando a cobertura de teste de um projeto Java usando o Apache Ignite como exemplo

Eu participo do desenvolvimento do projeto Apache Ignite de código-fonte aberto. Enquanto trabalhava no projeto, ficou interessante para mim avaliar a cobertura do teste e foi isso que aconteceu.



A cobertura de testes é a métrica mais popular usada na avaliação da qualidade dos testes de produtos.


Essa é uma das poucas métricas que permite identificar áreas que requerem atenção devido ao risco de perder um erro e também priorizar o trabalho em módulos ou componentes de projeto.


A maneira mais fácil de obter um relatório completo sobre a avaliação da cobertura de teste de um projeto Java é usar o corredor de cobertura incorporado ao IntelliJ IDEA . Ele permite configurar uma coleção de métricas em alguns cliques e executar testes com a geração de relatórios subsequente.


Testando no projeto Apache Ignite


O projeto Apache Ignite usa sua própria estrutura de teste para teste, implementada com base na JUnit 3. No momento da redação, o módulo principal do projeto contém ~ 82 mil testes, a maioria dos quais são componentes e exigem a criação de um cluster de vários nós, incluindo JVMs diferentes. com a preparação concomitante do ambiente.


Vale ressaltar que garantir a operacionalidade de uma base de regressão tão grande não é uma tarefa fácil. A comunidade monitora constantemente o status do produto e corrige os erros encontrados como parte da iniciativa Tornar o Teamcity Green Again .


Os recursos do projeto indicados não permitem executar todos os testes ao mesmo tempo em uma JVM pelos seguintes motivos:


  • possível erro OutOfMemoryError;
  • possível falha da JVM;
  • possíveis impasses;
  • incapacidade de iniciar o teste devido a um nó sem interrupção no teste anterior;
  • a execução levará três dias em um computador.

Tudo isso impossibilita o uso do IntelliJ IDEA para obter um relatório sobre todos os testes do projeto e requer uma abordagem especial para solucionar o problema.


Preparando e conduzindo a avaliação da cobertura do teste


Com base no trabalho realizado, foi escolhida a abordagem mais confiável para concluir a tarefa, contendo as seguintes etapas:


  1. Definindo um conjunto de classes de teste;
  2. Execução para cada classe de teste:
    2.1 inicie e execute um conjunto de testes de classe em uma JVM separada com um timer de watchdog que encerrará o encadeamento em caso de congelamentos ou problemas nos testes;
    2.2 operações para obter e salvar métricas de cobertura de teste;
    2.3 limpar o ambiente após a conclusão dos testes;
  3. Mesclagem de todas as métricas obtidas no parágrafo 2;
  4. Geração completa de relatórios.

Existem muitas ferramentas para avaliar a cobertura de testes, das quais as mais populares são:



Não vou me debruçar sobre suas diferenças; uma tabela visual comparando os recursos das ferramentas para avaliar a cobertura dos testes é apresentada aqui .


Para resolver o problema, a biblioteca JaCoCo foi escolhida para poder incorporar a solução no TeamCity , na qual a infraestrutura de teste existente do projeto Apache Ignite se baseia. O TeamCity pode funcionar imediatamente com a JaCoCo .


Para automatizar o algoritmo descrito, um script bash e Maven foram usados . A configuração do plug-in Jacoco Maven é implementada por um perfil Maven separado no pom.xml.


O perfil de configuração do plugin JaCoCo é fornecido abaixo e implica uma separação em 2 partidas separadas:


  1. Execute testes com o agente JaCoCo ( prepare-agent ) conectado para coletar métricas de cobertura de teste. A propriedade 'runDirectory' será transmitida pelo script na inicialização, o que permitirá salvar os resultados das execuções isoladamente;
  2. Mesclar resultados de execução ( mesclagem ) e geração de relatório ( relatório ).

Configuração do Maven JaCoCo
<profile> <id>coverage</id> <properties> <argLine> -ea \ -server \ -Xms1g \ -Xmx6g \ -XX:+HeapDumpOnOutOfMemoryError \ -XX:+AggressiveOpts \ -DIGNITE_UPDATE_NOTIFIER=false \ -DIGNITE_NO_DISCO_ORDER=true \ -DIGNITE_PERFORMANCE_SUGGESTIONS_DISABLED=true \ -DIGNITE_QUIET=false \ -Djava.net.preferIPv4Stack=true \ </argLine> <coverage.dataFile>${runDirectory}/coverage-reports/jacoco-ut.exec</coverage.dataFile> <coverage.outputDir>${runDirectory}/jacoco-ut</coverage.outputDir> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <configuration> </configuration> <executions> <execution> <id>default-test</id> <phase>test</phase> <goals> <goal>test</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.8.1</version> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <destFile>${coverage.dataFile}</destFile> </configuration> </execution> <execution> <id>post-merge</id> <phase>validate</phase> <goals> <goal>merge</goal> </goals> <configuration> <fileSets> <fileSet> <directory>${basedir}</directory> <includes> <include>results/*/coverage-reports/jacoco-ut.exec</include> </includes> </fileSet> </fileSets> <destFile>merged.exe</destFile> </configuration> </execution> <execution> <id>generate-report</id> <phase>validate</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${basedir}/merged.exe</dataFile> <outputDirectory>${basedir}/coverage-report</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> 

Abaixo está um script que implementa as etapas descritas anteriormente.


Script bash de controle
 #!/bin/bash #        DEVNOTES.txt # #    : ignite/modules/core # #   : 'nohup ./coverage.sh >/dev/null 2>&1 &' SCRIPT_DIR=$(cd $(dirname "$0"); pwd) echo "***** ." echo "*****   ..." tests=() while IFS= read -r -d $'\0'; do tests+=("$REPLY") done < <(find $SCRIPT_DIR/src/test/java/org/apache/ignite -type f -name "*Test*" ! -name "*\$*" ! -name "*Abstract*" ! -name "*TestSuite*" -print0) testsCount=${#tests[@]} echo "*****   ="$testsCount idx=0 for path in ${tests[@]} do idx=$((idx+1)) echo "*****  "$idx"  "$testsCount echo "*****  : "$path filename=$(basename -- "$path") filename="${filename%.*}" echo "*****  : "$filename runDir=$SCRIPT_DIR"/results/"$filename mkdir -p $runDir if [ "$(ls -A $runDir)" ]; then continue fi echo "*****  ..." timeout 30m mvn -P surefire-fork-count-1,coverage test -Dmaven.main.skip=true -Dmaven.test.failure.ignore=true -Dtest=$filename -DfailIFNoTests=false -DrunDirectory=$runDir echo "*****  ..." pkill java done #      mvn -X -P surefire-fork-count-1,coverage validate echo "***** ." 

A execução de todos os testes com avaliação de cobertura levou ~ 50 horas em um servidor dedicado: 4 vCPU, 8RAM, 50 SSD, Ubuntu x64 16.04 .


A abordagem descrita pode ser facilmente paralelizada a várias posições, se houver recursos disponíveis, o que reduzirá significativamente o tempo necessário para executar e obter uma avaliação da cobertura do teste. Após incorporar esta solução no TeamCity , o tempo de avaliação da cobertura do teste deve levar cerca de 2 horas.


Resultados


De acordo com os resultados do relatório, a cobertura das instruções do projeto é de ~ 61%.


Cobertura das instruções para os principais componentes:


  • Cache - 66%
  • Descoberta - 57%
  • Computação - 60%
  • Stream - 51%
  • Binário - 68%
  • Transações - 71%

Após analisar os resultados, ficou óbvio que todo o código ativo estava coberto, assim como o código para corrigir problemas típicos. Com esse kit de ferramentas, será possível expandir a cobertura para situações raras e atípicas, tornando o produto ainda mais confiável.


PS Relatório completo para revisão .

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


All Articles