Testes em Kotlin: unit, integration, e2e do A ao Z
\n\nO desenvolvimento moderno em Kotlin exige uma abordagem sistemática para testes. Independentemente de você estar escrevendo um backend com Ktor ou um aplicativo móvel para Android, a cobertura de código com testes de qualidade é a chave para a estabilidade e previsibilidade do projeto. Neste artigo, abordaremos três níveis de teste: testes unitários (unit), de integração (integration) e ponta a ponta (e2e). Você aprenderá quais ferramentas usar, como escrever testes eficazes em Kotlin e evitar erros comuns.
\n\n1. Testes unitários em Kotlin: a base de tudo
\n\nTestes unitários verificam o funcionamento de funções, classes ou métodos individuais isolados de dependências externas. Em Kotlin, o framework mais utilizado para isso é o JUnit 5 em conjunto com a biblioteca de mocks MockK.
\n\nConfiguração do projeto
\n\nAdicione as dependências no build.gradle.kts:
dependencies {\n testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")\n testImplementation("io.mockk:mockk:1.13.8")\n}\n\n\nExemplo de teste unitário
\n\nSuponha que temos um serviço simples para calcular descontos:
\n\nclass DiscountService {\n fun applyDiscount(price: Double, discountPercent: Int): Double {\n require(discountPercent in 0..100) { "A porcentagem de desconto deve estar entre 0 e 100" }\n return price * (100 - discountPercent) / 100\n }\n}\n\n\nTeste para este serviço:
\n\nimport org.junit.jupiter.api.Assertions.*\nimport org.junit.jupiter.api.Test\nimport org.junit.jupiter.api.assertThrows\n\nclass DiscountServiceTest {\n\n private val service = DiscountService()\n\n @Test\n fun `applyDiscount calcula corretamente o desconto de 50 por cento`() {\n val result = service.applyDiscount(1000.0, 50)\n assertEquals(500.0, result, 0.001)\n }\n\n @Test\n fun `applyDiscount lança exceção quando a porcentagem é inválida`() {\n assertThrows<IllegalArgumentException> {\n service.applyDiscount(100.0, 150)\n }\n }\n}\n\n\nDica: Use assertThrows para verificar exceções e nomeie os testes com strings claras — isso melhora a legibilidade dos relatórios.
Mocking com MockK
\n\nQuando sua classe depende de outros componentes (como um repositório ou API), use mocks:
\n\nclass OrderService(private val paymentGateway: PaymentGateway) {\n fun processOrder(amount: Double): String {\n if (paymentGateway.charge(amount)) {\n return "Pagamento bem-sucedido"\n }\n return "Erro no pagamento"\n }\n}\n\n// Teste\n@Test\nfun `processOrder retorna sucesso quando o pagamento é bem-sucedido`() {\n val mockGateway = mockk<PaymentGateway>()\n every { mockGateway.charge(any()) } returns true\n\n val service = OrderService(mockGateway)\n val result = service.processOrder(500.0)\n\n assertEquals("Pagamento bem-sucedido", result)\n verify { mockGateway.charge(500.0) }\n}\n\n\n2. Testes de integração: verificando a interação entre componentes
\n\nTestes de integração verificam como diferentes partes do sistema funcionam juntas: banco de dados, APIs externas, sistema de arquivos. Para Kotlin, o Testcontainers é uma excelente escolha — uma biblioteca para levantar contêineres Docker durante os testes.
\n\nExemplo com banco de dados PostgreSQL
\n\nAdicione as dependências:
\n\ntestImplementation("org.testcontainers:testcontainers:1.19.3")\ntestImplementation("org.testcontainers:postgresql:1.19.3")\n\n\nVamos escrever um teste para a camada DAO:
\n\nclass UserRepositoryIntegrationTest {\n\n companion object {\n private val postgres = PostgreSQLContainer<Nothing>("postgres:15").apply {\n withDatabaseName("testdb")\n withUsername("test")\n withPassword("test")\n start()\n