Introdução: por que o teste de integração com Docker?
Ao desenvolver aplicações modernas em Kotlin, especialmente microsserviços, é importante verificar como os componentes do sistema interagem entre si e com dependências externas (bancos de dados, filas de mensagens, caches). Os testes unitários isolam o código, mas não garantem que a aplicação funcione corretamente em um ambiente real.
O teste de integração com Docker resolve esse problema. O Docker permite levantar contêineres isolados com os serviços necessários (PostgreSQL, Redis, Kafka) imediatamente antes da execução dos testes e destruí-los automaticamente após a conclusão. Isso proporciona:
- Reprodutibilidade: os testes sempre são executados no mesmo ambiente.
- Isolamento: os contêineres não afetam o ambiente local do desenvolvedor.
- Velocidade: os contêineres são iniciados em segundos, não em minutos.
Neste artigo, vamos abordar como configurar o teste de integração de uma aplicação Kotlin usando Docker, a biblioteca Testcontainers e Docker Compose. Você receberá exemplos de código prontos e melhores práticas.
1. Configuração do projeto Kotlin para testes de integração
1.1. Dependências no build.gradle.kts
Para começar, adicione as dependências necessárias ao seu arquivo build.gradle.kts. Usaremos JUnit 5, Testcontainers e o driver para PostgreSQL:
dependencies { // Dependências principais da aplicação implementation("org.jetbrains.kotlin:kotlin-stdlib") implementation("org.postgresql:postgresql:42.7.1")
// Testes testImplementation("org.junit.jupiter:junit-jupiter:5.10.1") testImplementation("org.testcontainers:testcontainers:1.19.3") testImplementation("org.testcontainers:postgresql:1.19.3") testImplementation("org.testcontainers:junit-jupiter:1.19.3")}
tasks.test { useJUnitPlatform()}1.2. Estrutura básica da classe de teste
Vamos criar uma classe simples para o teste de integração que levantará um contêiner PostgreSQL:
import org.junit.jupiter.api.Testimport org.testcontainers.containers.PostgreSQLContainerimport org.testcontainers.junit.jupiter.Containerimport org.testcontainers.junit.jupiter.Testcontainersimport java.sql.DriverManager
@Testcontainersclass UserRepositoryIntegrationTest {
companion object { @Container val postgres = PostgreSQLContainer<Nothing>("postgres:15-alpine") .withDatabaseName("testdb") .withUsername("test") .withPassword("test") }
@Test fun `should connect to database and execute query`() { val jdbcUrl = postgres.jdbcUrl val username = postgres.username val password = postgres.password
val connection = DriverManager.getConnection(jdbcUrl, username, password) val statement = connection.createStatement()
statement.execute("CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(100))") statement.execute("INSERT INTO users (name) VALUES ('Alice')")
val resultSet = statement.executeQuery("SELECT COUNT(*) FROM users") resultSet.next() val count = resultSet.getInt(1)
assert(count == 1) println("Teste passou: encontrado $count usuários") }}Observe: a anotação @Testcontainers gerencia automaticamente o ciclo de vida do contêiner. Ele é iniciado antes de todos os testes na classe e interrompido após a conclusão deles.
2. Teste com vários contêineres (Docker Compose)
Em projetos reais, frequentemente são necessários vários serviços simultaneamente: banco de dados, Redis, Kafka. O Testcontainers suporta Docker Compose, o que permite descrever toda a infraestrutura em um único arquivo