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