Configurando Source Sets no Gradle 9 com jvm-test-suite
Ao concluirmos esta aula, nós:
- Seremos capazes de adicionar suítes de teste (test sets /
jvm-test-suite) customizadas ao nosso build no Gradle 9 (Kotlin DSL). - Saberemos como declarar as dependências exclusivas dos nossos testes de integração.
- Compreenderemos como executar apenas testes unitários, apenas testes de integração ou todos os testes com o Gradle.
Os Requisitos do Nosso Build Gradle
Os requisitos do nosso build no Gradle são:
- Nossos testes de integração devem ter um diretório de código-fonte separado: o diretório src/integration-test/kotlin (ou src/integrationTest/kotlin) deve conter o código-fonte dos nossos testes de integração.
- Nossos testes de integração devem ter um diretório de recursos separado: o diretório src/integration-test/resources deve conter os recursos dos nossos testes de integração.
- Devemos ser capazes de executar apenas os testes unitários, apenas os testes de integração ou todos os testes juntos.
- Se um teste de integração falhar, o nosso build também deve falhar.
- Os testes unitários e os testes de integração devem gerar relatórios HTML em diretórios separados (para que um relatório não sobrescreva o outro).
Vamos começar adicionando um novo conjunto de testes (test suite) ao nosso build Gradle!
Adicionando Suítes de Teste ao Nosso Build Gradle
No Gradle antigo (Gradle 3.x a 6.x), precisávamos de um plugin externo de terceiros (org.unbroken-dome.test-sets) para criar source sets de teste adicionais sem código boilerplate.
A partir do Gradle 7.3+ e de forma nativa e padronizada no Gradle 9 (Kotlin DSL), o próprio Gradle inclui o plugin oficial jvm-test-suite embutido na distribuição! Ainda assim, vamos compreender como ambos funcionam: a evolução histórica do testSets para a DSL declarativa moderna testing { suites { ... } } do jvm-test-suite.
Passo 1: Aplicando os Plugins no build.gradle.kts
Para usarmos o suporte nativo a múltiplas suítes de teste na JVM com Kotlin e JUnit Jupiter 6, declaramos os plugins no bloco plugins do nosso arquivo build.gradle.kts:
|
|
Passo 2: Configurando os Diretórios de Código-Fonte e Recursos dos Testes de Integração
No plugin clássico org.unbroken-dome.test-sets (em Groovy), definíamos testSets { integrationTest { dirName = 'integration-test' } }.
No Gradle 9 com jvm-test-suite, registramos uma nova suíte chamada integrationTest do tipo JvmTestSuite dentro do bloco testing.suites. Por convenção, o Gradle cria os diretórios src/integrationTest/kotlin e src/integrationTest/resources. Se quisermos usar exatamente os diretórios com hífen (src/integration-test/kotlin e src/integration-test/resources), configuramos o sources da suíte:
|
|
Vamos descobrir agora como declarar as dependências dos nossos testes de integração!
Declarando as Dependências dos Nossos Testes de Integração
Quando adicionamos a suíte integrationTest ao nosso build, o Gradle cria configurações de dependência dedicadas para ela:
integrationTestImplementation: Contém as dependências necessárias para compilar e rodar nossos testes de integração (comospring-boot-starter-test,testcontainers,mockk, etc.).integrationTestRuntimeOnly: Contém as dependências necessárias apenas em tempo de execução dos testes de integração (como drivers JDBC ou ojunit-platform-launcher).
Além disso, para que os nossos testes de integração possam acessar as classes de produção do projeto (main) e também reutilizar utilitários ou builders de teste unitário, podemos associar o classpath de integrationTest ou declarar as dependências diretamente dentro do bloco dependencies da suíte integrationTest:
|
|
Ou, se preferirmos usar o bloco dependencies { ... } tradicional de nível superior do build.gradle.kts, podemos estender as configurações de testImplementation:
|
|
Escrevendo um Teste de Integração Simples
A classe SampleIntegrationTest contém um teste de integração que imprime a string "Tag: integrationTest" na saída padrão (System.out). Para categorizarmos esta classe de teste no JUnit Jupiter 6, nós a anotamos com @Tag("integrationTest") (ou com a nossa meta-anotação @IntegrationTest):
|
|
A seguir, precisamos configurar a task integrationTest que executa nossos testes de integração.
Configurando a Task que Executa Nossos Testes de Integração
Quando registramos a suíte integrationTest, o Gradle cria automaticamente uma task do tipo Test chamada integrationTest. Para atendermos a todos os requisitos do nosso build, vamos configurar a ordem de execução, os diretórios de relatórios HTML e a filtragem de tags do JUnit Platform em três passos:
Primeiro, garantimos que os testes unitários rodem antes dos testes de integração e que os testes de integração façam parte do ciclo de verificação da task check (e consequentemente da task build):
- Fazemos a task
checkdepender deintegrationTest(check { dependsOn(integrationTest) }), de modo quegradle buildougradle checkfalhe se qualquer teste de integração falhar. - Configuramos
shouldRunAfter(test)(oumustRunAfter(test)) na taskintegrationTest. Isso garante que, quando ambos os testes forem solicitados no mesmo comando, os testes unitários rápidos sempre rodem primeiro!
Segundo, garantimos que os relatórios HTML e XML dos testes unitários e dos testes de integração sejam gravados em diretórios separados (build/reports/tests/test e build/reports/tests/integrationTest). No Gradle 9 com jvm-test-suite, o Gradle já separa esses diretórios automaticamente por nome da task, mas podemos deixar explícito em tasks.withType<Test>().
Terceiro, configuramos o JUnit Platform (useJUnitPlatform) em cada task para filtrar por @Tag (unitTest vs integrationTest) e exibir o System.out (showStandardStreams = true).
Após realizarmos todas essas configurações, nosso arquivo build.gradle.kts completo fica assim:
|
|
Executando Testes Unitários e de Integração no Gradle
Podemos executar nossos testes no terminal usando uma das três opções a seguir:
Primeiro, se quisermos rodar apenas os testes unitários, temos duas alternativas:
- Executar diretamente a task
test:./gradlew clean test - Executar o build completo excluindo a task de integração com a flag
-x:./gradlew clean build -x integrationTest
Quando executamos ./gradlew clean build -x integrationTest, vemos a seguinte saída:
|
|
Segundo, se quisermos rodar apenas os testes de integração, temos duas alternativas:
- Executar diretamente a task
integrationTest:./gradlew clean integrationTest - Executar o build completo excluindo os testes unitários:
./gradlew clean build -x test
Quando executamos ./gradlew clean build -x test, vemos a seguinte saída:
|
|
Terceiro, se quisermos rodar todos os testes (unitários seguidos dos de integração):
- Executar
./gradlew clean test integrationTest - Ou simplesmente executar
./gradlew clean build
Quando executamos ./gradlew clean build, vemos que os testes unitários rodam primeiro e os testes de integração rodam logo em seguida:
|
|
Resumo
Esta aula nos ensinou três coisas fundamentais:
- Podemos adicionar suítes de teste isoladas (
integrationTest) ao nosso build no Gradle 9 usando o plugin oficial embutidojvm-test-suite(substituto moderno do antigo pluginorg.unbroken-dome.test-sets). - Cada suíte de teste possui seu próprio bloco
dependenciese configurações (integrationTestImplementation,integrationTestRuntimeOnly), permitindo isolar bibliotecas pesadas de integração do classpath dos testes unitários. - Ao vincularmos a suíte
integrationTestà taskcheckcomshouldRunAfter(test), garantimos que os testes unitários rápidos sempre rodem antes dos testes de integração e que relatórios HTML sejam gerados em diretórios independentes.