Olá pessoal dessa vez mostro de forma resumida como configurar TestContainers com Kotlin e SpringBoot.
Cenário
Em um projeto SpringBoot é bem comum configurarmos o banco H2 com o intuito de usá-lo como banco de dados de testes, porém nem sempre essa abordagem é eficaz. Quando precisamos ter uma alta fidelidade com o banco de produção ou usar-mos alguma funcionalidade inerente do mesmo, como “functions” por exemplo, a escolha do H2 deixa a desejar. Para esses casos específicos uma escolha válida seria fazer uso do TestContainers.
O que é o TestContainers?
É uma biblioteca Java que suporta testes JUnit, fornecendo instâncias leves e descartáveis de bancos de dados comuns, navegadores Selenium ou qualquer outra coisa que possa ser executada em um contêiner Docker.
Pré-Requisito para Uso
Ter o Docker instalado na sua máquina.
Contras
Será em média mais lento do que o H2.
Contexto do Exemplo
- Será um projeto em Kotlin + SpringBoot + PostgreSQL + TestContainers
- Será um exemplo curto e direto ao ponto.
- O domínio se trata de uma lojinhas de pasteis, onde só o contexto dos nomes dos pasteis será abordado.
Início
Crie o projeto no Spring Initializr (Linguagem: Kotlin. Spring Boot: 3.2.5. Java: 17)
Abra o projeto no Intellij (ou no editor de sua preferencia 😌).
Confirme se as dependências no arquivo “build.gradle” estão conforme abaixo:
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.jetbrains.kotlin:kotlin-reflect'
runtimeOnly 'org.postgresql:postgresql'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.boot:spring-boot-testcontainers'
testImplementation 'org.testcontainers:junit-jupiter'
testImplementation 'org.testcontainers:postgresql'
}
Dentro da pasta do projeto, no meu caso “com.example.testcontainers”, crie uma data class Pastel conforme abaixo:
package com.example.testcontainers import jakarta.persistence.Column import jakarta.persistence.Entity import jakarta.persistence.GeneratedValue import jakarta.persistence.GenerationType import jakarta.persistence.Id @Entity(name = "pasteis") data class Pastel( @Id @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Int = 0, @Column val name: String = "" )
package com.example.testcontainers
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface PastelRepository: JpaRepository<Pastel, Int> {}
Dentro da pasta “resources” renomeie o arquivo application.properties para application.yml(aqui é só frescura mesmo 🙃) e adicione o conteúdo abaixo:
spring:
application:
name: testcontainers
datasource:
url: jdbc:postgresql://${postgres.url}/${postgres.database}
username: ${postgres.username}
password: ${postgres.password}
driver-class-name: org.postgresql.Driver
postgres:
url: localhost:${postgres.port}
username: postgres
password: postgres
database: tes-cont-db
spring:
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
package com.example.testcontainers
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.FilterType
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.DynamicPropertyRegistry
import org.springframework.test.context.DynamicPropertySource
import org.testcontainers.containers.PostgreSQLContainer
import org.testcontainers.junit.jupiter.Container
import org.testcontainers.junit.jupiter.Testcontainers
import java.lang.annotation.Inherited
@SpringBootTest
@Testcontainers
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class ApplicationTests {
@Autowired
lateinit var pastelRegistry: PastelRepository
companion object {
@Container
val db = PostgreSQLContainer<Nothing>("postgres:latest").apply {
this.withDatabaseName("tes-cont-db")
this.withUsername("postgres")
this.withPassword("postgres")
}
@JvmStatic
@DynamicPropertySource
fun setDatasourceProperties(registry: DynamicPropertyRegistry) {
registry.add(
"postgres.port"
) { db.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT) }
}
}
@Test
fun contextLoads() {
val pastel = Pastel(name = "Carne")
pastelRegistry.save(pastel)
val pasteis = pastelRegistry.findAll()
Assertions.assertTrue(pasteis.isNotEmpty())
Assertions.assertEquals(pastel.name, pasteis[0].name)
}
}
É praticamente tudo o que você precisa para realizar um teste simples usando TestContainers
Agora é só executar o teste e ver a mágica acontecer
Conclusão
O uso do TestContainers pode ser de grande ajuda para quando temos queries complexas ou usamos outras características particulares do banco de dados. Com isso garantimos similaridade entre o banco de produção e o de testes. Dessa forma podemos evitar falsos positivos/negativos devido a diferenças de cada banco.
Visite nosso canal no Youtube deixe seu comentário, sua inscrição e joinha.
Outra Leitura que pode te interessar: 3 Livros Que Todo Dev Precisa Ler



