DEV Community

Isaias Velasquez
Isaias Velasquez

Posted on

How to usar Testcontainers para testes de integracao que prestam

Testcontainers e uma biblioteca que sobe containers Docker dentro dos seus testes de integracao. Em vez de mockar o banco de dados, voce usa um banco de verdade que nasce e morre com o teste.

Adicione a dependencia no build.gradle.kts:

testImplementation("org.testcontainers:testcontainers:1.20.0")
testImplementation("org.testcontainers:postgresql:1.20.0")
testImplementation("org.testcontainers:junit-jupiter:1.20.0")
Enter fullscreen mode Exit fullscreen mode

O teste mais simples com PostgreSQL:

@Testcontainers
@SpringBootTest
class UsuarioRepositoryTest {

    companion object {
        @Container
        val postgres = PostgreSQLContainer<Nothing>("postgres:16").apply {
            withDatabaseName("testdb")
            withUsername("test")
            withPassword("test")
        }
    }

    @Autowired
    lateinit var repo: UsuarioRepository

    @Test
    fun deveSalvarUsuario() {
        val usuario = Usuario(nome = "Joao", email = "joao@email.com")
        val salvo = repo.save(usuario)
        assertThat(salvo.id).isNotNull()
    }
}
Enter fullscreen mode Exit fullscreen mode

O Testcontainers sobe um container PostgreSQL antes dos testes e derruba depois. Nao precisa de banco instalado na maquina.

Para configurar o Spring Boot automaticamente:

@DynamicPropertySource
fun configurarProps(registry: DynamicPropertyRegistry) {
    registry.add("spring.datasource.url", postgres::getJdbcUrl)
    registry.add("spring.datasource.username", postgres::getUsername)
    registry.add("spring.datasource.password", postgres::getPassword)
}
Enter fullscreen mode Exit fullscreen mode

Isso substitui as properties do application.properties pela URL do container que acabou de subir.

Para Redis:

@Container
val redis = GenericContainer<Nothing>("redis:7").apply {
    withExposedPorts(6379)
}

@DynamicPropertySource
fun redisProps(registry: DynamicPropertyRegistry) {
    registry.add("spring.redis.host", redis::getHost)
    registry.add("spring.redis.port") { redis.getMappedPort(6379).toString() }
}
Enter fullscreen mode Exit fullscreen mode

Testcontainers tambem suporta Kafka:

@Container
val kafka = KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:7.6.0"))

@DynamicPropertySource
fun kafkaProps(registry: DynamicPropertyRegistry) {
    registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers)
}
Enter fullscreen mode Exit fullscreen mode

Para reaproveitar o container entre varios testes:

@Testcontainers
abstract class AbstractIntegrationTest {
    companion object {
        @Container
        val postgres = PostgreSQLContainer<Nothing>("postgres:16").apply {
            withDatabaseName("testdb")
            withUsername("test")
            withPassword("test")
            start()
        }

        @DynamicPropertySource
        @JvmStatic
        fun props(registry: DynamicPropertyRegistry) {
            registry.add("spring.datasource.url", postgres::getJdbcUrl)
            registry.add("spring.datasource.username", postgres::getUsername)
            registry.add("spring.datasource.password", postgres::getPassword)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Agora toda classe que estender AbstractIntegrationTest ganha um banco PostgreSQL real.

Dica: se os testes ficarem lentos, use withReuse(true) no container. Isso mantem o container vivo entre execucoes. So nao esqueca de configurar testcontainers.reuse.enable=true em ~/.testcontainers.properties.

That's all for now.
Thanks for reading!

Top comments (0)