01. Introdução aos Testes de Integração
Ao concluirmos todas as aulas deste tópico, nós:
- Compreenderemos por que não devemos misturar testes unitários e testes de integração no mesmo diretório ou tarefa de execução.
- Saberemos como isolar e executar nossos testes de integração em fases dedicadas do ciclo de vida do build usando o plugin nativo
jvm-test-suitedo Gradle 9.8.2 (e perfis no Maven). - Saberemos como configurar do zero um ambiente reproduzível e profissional de testes de integração com Spring Boot 4, Testcontainers 2.0.5 e PostgreSQL.
Como Configurar o Ambiente para Testes de Integração com Testcontainers e PostgreSQL
Testes de integração modernos em aplicações Spring Boot exigem fidelidade aos componentes de infraestrutura de produção. No passado, era comum utilizar bancos em memória como H2 ou HSQLDB nos testes de integração. No entanto, essa abordagem frequentemente oculta diferenças críticas de dialeto SQL, tipos de dados proprietários (como JSONB, UUID nativo e arrays no PostgreSQL), funções de data/hora, restrições de integridade e características de concorrência e locking.
Com Testcontainers e o suporte nativo do Spring Boot 4 (@ServiceConnection), nós executamos nossos testes de integração contra instâncias reais e efêmeras do PostgreSQL provisionadas sob demanda em contêineres Docker.
Abaixo, apresentamos o guia prático completo e repetível para criar e estruturar esse ambiente no seu projeto com Gradle 9.8.2 (Kotlin DSL).
1. Requisitos de Sistema e Pré-requisitos de Runtime
Antes de executar testes de integração com Testcontainers, precisamos garantir que o runtime de contêineres e as ferramentas de desenvolvimento estejam instalados e em execução.
Requisitos Obrigatórios
-
JDK 21 ou superior (Gerenciado via SDKMAN):
- O Spring Boot 4 exige no mínimo o Java 21 (ou Java 25 LTS).
- Instale e ative facilmente com o SDKMAN:
1 2 3sdk install java 21.0.6-tem sdk use java 21.0.6-tem java -version
-
Docker Engine ou Docker Desktop:
- É necessário ter um daemon do Docker acessível via socket local (
/var/run/docker.sock). - Verifique se o daemon está respondendo com o comando:
1docker info - Se o comando retornar as informações do daemon sem erros de permissão, seu ambiente Docker está pronto.
- É necessário ter um daemon do Docker acessível via socket local (
[!NOTE] No Linux, certifique-se de que o seu usuário pertence ao grupo
dockerpara evitar a necessidade desudo:
1 2sudo usermod -aG docker $USER # E reinicie sua sessão do terminal ou execute: newgrp docker
Utilizando Podman como Alternativa ao Docker
Se você utiliza o Podman em substituição ao Docker, ative o serviço do socket de compatibilidade Docker da sua sessão de usuário:
|
|
Valide a conectividade com:
|
|
2. Gerando o Projeto via curl com start.spring.io
Podemos iniciar um novo projeto Spring Boot completo diretamente pela linha de comando usando a API REST do Spring Initializr com o utilitário curl.
Execute o comando a seguir no terminal para gerar e descompactar o projeto base:
|
|
Detalhamento das Flags do Spring Initializr
type=gradle-project-kotlin: Seleciona o projeto Gradle configurado com Kotlin DSL (build.gradle.kts).language=kotlin: Define o Kotlin como linguagem de programação do código-fonte.bootVersion=4.1.2: Versão moderna do Spring Boot.javaVersion=21: Define o target de compilação da JVM.dependencies:web: Adiciona o Spring MVC (spring-boot-starter-web) e Tomcat embutido.data-jpa: Adiciona o Spring Data JPA e Hibernate para persistência relacional.postgresql: Driver JDBC oficial do PostgreSQL.testcontainers: Adiciona a biblioteca base do Testcontainers e os starters de integração de teste do Spring Boot.flyway: Gerenciamento e versionamento automatizado de schemas SQL.
3. Configuração no build.gradle.kts com jvm-test-suite no Gradle 9.8.2
No Gradle 9.8.2, o plugin nativo jvm-test-suite é o padrão recomendado e canônico para declarar e gerenciar múltiplas suítes de teste isoladas. Ele elimina completamente a necessidade de plugins de terceiros ou configurações manuais complexas de sourceSets e tasks Test.
Objetivos do Nosso Build
- Manter os testes unitários rápidos e isolados no diretório padrão
src/test/kotlin. - Criar uma suíte dedicada
integrationTestcom código emsrc/integration-test/kotline recursos emsrc/integration-test/resources. - Adicionar dependências exclusivas de teste de integração (Testcontainers PostgreSQL,
@ServiceConnection, Flyway e AssertJ) apenas à suíte de integração. - Garantir que os testes unitários sejam executados antes dos testes de integração (
shouldRunAfter(test)). - Vincular a suíte de integração à verificação geral do build (
check.dependsOn(integrationTest)). - Gerar relatórios HTML e XML separados para cada suíte.
O Arquivo build.gradle.kts Completo
Substitua o conteúdo do seu arquivo build.gradle.kts pela seguinte configuração declarativa e idiomática:
|
|
O Papel do Spring Boot Testcontainers e @ServiceConnection
Observe a dependência:
|
|
Essa biblioteca introduz a anotação @ServiceConnection. Tradicionalmente, ao usar Testcontainers no Spring Boot, éramos obrigados a usar métodos estáticos complexos com @DynamicPropertySource para mapear manualmente a porta efêmera atribuída pelo Docker às propriedades spring.datasource.url, spring.datasource.username e spring.datasource.password:
|
|
Com o @ServiceConnection, basta anotar o campo do container PostgreSQLContainer! O Spring Boot detecta automaticamente a conexão do container em tempo de inicialização do teste e injeta todas as configurações de DataSource sem nenhum código boilerplate.
4. Estruturando os Diretórios e Criando o Primeiro Teste
Vamos agora criar a estrutura física de diretórios e escrever um teste de integração real para verificar o provisionamento do PostgreSQL.
Passo 1: Criando os Diretórios do Test Suite
No terminal da raiz do projeto, crie os diretórios de código e recursos da suíte integrationTest:
|
|
Passo 2: Criando o Script Flyway de Migração
Crie o arquivo src/integration-test/resources/db/migration/V1__create_customers_table.sql:
|
|
Passo 3: Criando o Arquivo de Propriedades de Teste
Crie o arquivo src/integration-test/resources/application-integration-test.yml:
|
|
Passo 4: Escrevendo a Classe de Teste de Integração
Crie o arquivo src/integration-test/kotlin/com/testwithspring/demo/DatabaseIntegrationTest.kt:
|
|
5. Executando e Validando com ./gradlew integrationTest
Com a suíte configurada, agora podemos executar nossos testes no terminal através do Gradle Wrapper.
1. Executando Estritamente a Suíte de Integração
Para rodar apenas os testes de integração (subindo o container PostgreSQL efêmero sem executar os testes unitários):
|
|
Na saída do terminal, você observará o log do Testcontainers gerenciando o ciclo de vida do container:
|
|
2. Executando Apenas os Testes Unitários
Para um ciclo de feedback ultra-rápido durante o desenvolvimento de código isolado (sem inicializar o Docker):
|
|
3. Executando a Validação Completa do Projeto
Quando executamos a task padrão check ou build, o Gradle executa ambas as suítes respeitando a ordem definida (shouldRunAfter(test)):
|
|
O Gradle primeiro executa a task test (testes unitários). Se todos passarem, ele prossegue para integrationTest. Se qualquer teste falhar, o build é interrompido imediatamente.
4. Pulando a Suíte de Integração em Builds Rápidos
Se você deseja executar o build completo mas pular os testes que dependem de contêineres Docker, use a flag -x:
|
|
5. Localização dos Relatórios Gerados
Ao término da execução, consulte os relatórios HTML detalhados gerados em diretórios independentes:
- Relatório de Testes Unitários:
build/reports/tests/test/index.html - Relatório de Testes de Integração:
build/reports/tests/integrationTest/index.html
6. Resolução de Problemas Comuns (Troubleshooting)
Erro: Could not find a valid Docker environment
- Causa: O daemon do Docker não está rodando ou o processo do Gradle não possui permissões no arquivo de socket
/var/run/docker.sock. - Solução:
- Inicie o serviço com
sudo systemctl start docker(no Linux) ou abra o Docker Desktop. - Garanta permissões com
sudo usermod -aG docker $USER. - No caso de Podman, confirme que
export DOCKER_HOST="unix://$XDG_RUNTIME_DIR/podman/podman.sock"está configurado no seu terminal.
- Inicie o serviço com
Erro: TimeoutException: Timed out waiting for container to start
- Causa: O download da imagem do PostgreSQL demorou mais que o timeout padrão do Testcontainers (comum na primeira execução com conexões lentas).
- Solução:
Faça o pull manual prévio da imagem utilizada:
1docker pull postgres:17-alpine
Otimização: Reutilização de Contêineres no Desenvolvimento Local
Durante o desenvolvimento no dia a dia, subir e derrubar o container a cada execução de teste pode consumir tempo desnecessário. O Testcontainers permite reutilizar contêineres entre execuções locais:
-
Crie o arquivo
~/.testcontainers.propertiesno seu diretório home:1testcontainers.reuse.enable=true -
Na sua classe de teste Kotlin, adicione
.withReuse(true)ao container:1 2 3 4 5val postgres = PostgreSQLContainer("postgres:17-alpine") .withDatabaseName("testdb") .withUsername("testuser") .withPassword("secret") .withReuse(true)
Com isso, o container permanecerá em execução em segundo plano entre os disparos de ./gradlew integrationTest, acelerando a inicialização para menos de 1 segundo!
Conteúdo
-
Isolando Testes Unitários e de Integração no Build
Aprenda a adicionar diretórios de código e recursos dedicados para testes de integração, usar @Tag("integrationTest") no JUnit Jupiter 6 e isolar a execução com perfis Maven (Surefire vs Failsafe).
-
Configurando Source Sets no Gradle 9 com jvm-test-suite
Aprenda a configurar suítes e diretórios isolados para testes de integração no Gradle 9 (Kotlin DSL), gerenciando dependências dedicadas, relatórios HTML separados e ordem de execução.