Blog

Cobertura de código em projetos Java multi-módulo

Abril 18, 2019 | Equifax

Olá! Damos as boas-vindas ao primeiro artigo do nosso time de developers da Konduto! Temos uma equipe apaixonada por tecnologia e apaixonada por aprender, ensinar, compartilhar conhecimento - você já deve ter lido um pouquinho sobre alguns de nós na série Quem faz a Konduto . Pois bem... mas como compartilhar o conhecimento que nasce nas nossas reuniões de dev e nas nossas reuniões de daily? Bom, o Blog da Konduto é o canal de comunicação mais importante da nossa empresa, e por isso pedimos uma licencinha para o pessoal do marketing para, periodicamente, escrevermos alguns artigos para a nossa comunidade de programadores, desenvolvedores e maníacos por tecnologia. O pessoal topou, e agora temos a nossa seção aqui no blog! Espero que gostem!

Introdução

Todos sabem que não tem como ter um código de qualidade sem testes automatizados. E, para garantir o bom funcionamento da aplicação, mais importante do que ter testes é ter testes de qualidade. Como saber que os testes que escrevi para a minha aplicação são suficientes? Esta é uma longa discussão, já que podemos olhar para a qualidade de código a partir de vários ângulos diferentes, desde os requisitos de negócio, passando por aspectos técnicos e comportamento do usuário. Um destes ângulos é a cobertura de código. Ou seja, o quanto do seu código está coberto por testes. Mais especificamente: quantas e quais instruções da aplicação são visitadas durante os testes. Apenas garantir que o código está coberto por testes não vai te salvar de todos problemas, mas já é um primeiro passo em direção a menos bugs. Para Java existem uma porção de libs que calculam a cobertura automaticamente para você. O que vamos mostrar aqui é como configurar cobertura de testes em um projeto Java, especialmente para o caso em que a aplicação é composta de múltiplos módulos. Aqui vamos assumir que você está utilizando o Maven  como gerenciador de projeto. Um projeto Java multi-módulo gerenciado pelo Maven possui uma estrutura de diretórios similar a abaixo:

sample-project/
 ├── module1/
 │ ├── src/
 │ │ ├── main/
 │ │ └── test/
 │ └── pom.xml
 ├── module2/
 │ ├── src/
 │ │ ├── main/
 │ │ └── test/
 │ └── pom.xml
 └── pom.xml

Temos a pasta raiz sample-project , ela tem um arquivo Maven pom.xml  que descreve o projeto geral. E vemos dois módulos module1 e module2 . Cada um com seu respectivo pom.xml , e onde src/  é guarda o código da aplicação e test/  o código de teste. A cobertura de testes é quanto os testes em test/ cobrem o código contido em src/ . Vale lembrar que nesta estrutura toda dependência e tarefa Maven definida no pom.xml  raiz é replicada em todos os módulos por padrão (a não ser que explicitado o contrário). Se você quiser saber mais sobre projetos multi-módulo em Maven, este artigo  cobre mais detalhes sobre o assunto. - Para os apressados, colocamos o projeto acima já configurado com cobertura neste repositório .

Calculando a cobertura de código com o JaCoCo

O JaCoCo  é um dentre vários sistemas que sabe calcular a cobertura de código de uma suíte de testes Java. Além de te dar a porcentagem do código coberto por testes, ele fornece uma porção de outros indicadores interessantes, como porcentagem das ramificações do código ( if/else , for  etc) visitadas, classes, métodos, etc. O primeiro passo é adicionar o plugin do JaCoCo no pom.xml  do diretório raiz. É importante que a dependência seja adicionada com escopo test para garantir que ela só seja incluída durante a execução dos testes:

xml
<properties>
 <jacoco.version>0.8.3</jacoco.version>
</properties>

<dependencies>
 ... demais dependências ...
 <dependency>
 <groupId>org.jacoco</groupId>
 <artifactId>jacoco-maven-plugin</artifactId>
 <version>${jacoco.version}</version>
 <type>maven-plugin</type>
 <scope>test</scope>
 </dependency>
</dependencies>

Em seguida, configuramos para o JaCoCo ser iniciado e executado durante a etapa de build usando a configuração mais básica possível:

xml
<build>
 <plugins>
 <plugin>
 <groupId>org.jacoco</groupId>
 <artifactId>jacoco-maven-plugin</artifactId>
 <version>${jacoco.version}</version>
 <executions>
 <execution>
 <id>default-prepare-agent</id>
 <goals>
 <goal>prepare-agent</goal>
 </goals>
 </execution>
 <execution>
 <id>default-report</id>
 <goals>
 <goal>report</goal>
 </goals>
 </execution>
 </executions>
 </plugin>
 </plugins>
</build>

O passo default-prepare-agent  inicia o sistema do JaCoCo. O passo default-report  executa o JaCoCo para gerar o relatório de cobertura padrão. O JaCoCo possui várias de opções e tipos de relatórios dependendo da sua necessidade. Neste link  você encontra a explicação do que é cada uma das configurações possíveis do plugin Maven do JaCoCo. O relatório básico gerado pelo JaCoCo vem em um formato pouco amigável para leitura humana, otimizado para parseamento programático. Para a nossa sorte, o JaCoCo vem com uma função que consegue transformar este relatório em uma página HTML de fácil visualização, contendo todos os indicadores calculados, como porcentagem de instruções/classes/métodos/ramificações cobertas. Para gerar o relatório em HTML, inclua na etapa reporting do pom.xml  o plugin do JaCoCo com a seguinte configuração:

xml
<reporting>
 <plugins>
 <plugin>
 <groupId>org.jacoco</groupId>
 <artifactId>jacoco-maven-plugin</artifactId>
 <version>${jacoco.version}</version>
 <reportSets>
 <reportSet>
 <reports>
 <report>report</report>
 </reports>
 </reportSet>
 </reportSets>
 </plugin>
 </plugins>
</reporting>

Pronto! Quando você buildar o projeto via mvn install  serão gerados os reports de cada módulo nos diretórios seus respectivos diretório target/site/ . Acesse os links abaixo para visualizar os relatórios em HTML:

sample-project/module1/target/site/jacoco/index.html

sample-project/module2/target/site/jacoco/index.html

Agregando a cobertura do projeto em relatório único

Você deve ter percebido que, seguindo os passos acima, geramos um relatório para cada módulo. Isso já ajuda, mas não é tão prático para visualizar o projeto todo. Existe um jeito de agregar os relatórios gerados em um único HTML. A partir da versão 0.7.7 , o JaCoCo vem com um método que sabe fazer essa agregação: jacoco:report-aggregate . Quando chamado, este método busca dentro do diretório de todas as dependências do módulo buildado por relatórios de cobertura e os agrega em um relatório único. Perceba que ele só sabe agregar relatórios de módulos listados entre as <dependencies> do projeto. Portanto, a estratégia que vamos usar aqui é criar um módulo especial que tem como dependências todos os demais módulos do projeto. Vamos chamar este módulo de coverage . Para criar este módulo, digite o comando abaixo a partir do diretório raiz do projeto:

mvn archetype:generate \
 -DarchetypeGroupId=org.apache.maven.archetypes \
 -DarchetypeArtifactId=maven-archetype-quickstart \
 -DarchetypeVersion=RELEASE

Ele vai te pedir alguns inputs, como groupId , artifactId  e a versão do módulo. No nosso exemplo demos o nome de coverage  ao artifactId . Agora a estrutura de diretórios do seu projeto deve se parecer com a seguinte:

sample-project/
 ├── module1/
 │ ├── src/
 │ └── pom.xml
 ├── module2/
 │ ├── src/
 │ └── pom.xml
 ├── coverage/
 │ ├── src/
 │ └── pom.xml
 └── pom.xml

Adicione os demais módulos do projeto como dependências do coverage  no coverage/pom.xml :

xml
 <dependencies>
 <dependency>
 <groupId>com.konduto</groupId>
 <artifactId>module1</artifactId>
 <version>1.0-SNAPSHOT</version>
 <scope>provided</scope>
 </dependency>
 <dependency>
 <groupId>com.konduto</groupId>
 <artifactId>module2</artifactId>
 <version>1.0-SNAPSHOT</version>
 <scope>provided</scope>
 </dependency>
 </dependencies>

Configure a tarefa jacoco:report-aggregate  para rodar durante a etapa build  no coverage/pom.xml :

xml
 <build>
 <plugins>
 <plugin>
 <groupId>org.jacoco</groupId>
 <artifactId>jacoco-maven-plugin</artifactId>
 <version>${jacoco.version}</version>
 <executions>
 <execution>
 <id>report-aggregate</id>
 <phase>prepare-package</phase>
 <goals>
 <goal>report-aggregate</goal>
 </goals>
 <configuration>
 <title>JaCoCo</title>
 <footer>Code Coverage Report for JaCoCo ${project.version}</footer>
 </configuration>
 </execution>
 </executions>
 </plugin>
 </plugins>
 </build>

Pronto. Da próxima vez que você rodar um mvn install  no diretório raiz você deve ver o relatório completo de cobertura do projeto no endereço abaixo:

sample-project/coverage/target/site/jacoco-aggreagate/index.html

Sempre que novos módulos forem criados dentro do projeto lembre-se de adicioná-los como dependência do projeto coverage . Além disso, você deve garantir que o módulo coverage  seja sempre o último a ser buildado. Ou seja, que na entrada <modules> no pom.xml  raiz, o projeto coverage  sempre seja último:

xml
 <modules>
 <module>module1</module>
 <module>module2</module>
 <module>coverage</module> <!-- Deve ser sempre o último -->
 </modules>

Veja o projeto completo na minha página do Gitlab ! Como foi sua experiência configurando cobertura de código no seu projeto? Você teve algum problema inesperado, ou descobriu algo que pode ter ficado de fora? Este passo a passo só funciona no Maven, sabe como funcionaria no Gradle? Conta para a gente nos comentários como foi, ou entre em contato diretamente com a gente no oi@konduto.com .

Newsletter da Konduto