Integração com TestContainers no Springboot 3.1

Valdemar Jr
Livelo
4 min readJul 27, 2023

--

O Springboot framework é um dos frameworks mais populares do mercado, como solução para criação de arquiteturas baseadas em microsserviços. Com a constante evolução do Java, que acaba de lançar a release da versão 17, o projeto Springboot lança uma nova versão para acompanhar as evoluções da linguagem.

Agora em sua versão 3.1, o Springboot apresenta algumas novidades interessantes, como:

Neste artigo iremos focar apenas na integração usando TestContainers.

TestContainers é uma biblioteca que fornece, de forma eficiente e descartável, instâncias de banco de dados, Selenium, Message Brokers ou quaisquer dependências necessárias que possam ser executadas usando docker. Isto é útil, por exemplo, para auxiliar no desenvolvimento de testes de integração.

Na nova versão do Springboot 3.1, agora é possível configurar o TestContainers para que possa disponibilizar recursos externos em tempo de desenvolvimento, como por exemplo, um banco de dados.

Neste artigo iremos usar o TestContainers para criar um ambiente local de desenvolvimento que disponibiliza um serviço de banco de dados PostgreSQL.

Para isso, é preciso adicionar as seguintes dependências no pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>

Dica: O Spring Initializr pode ser utilizado para adicionar essas dependências ao criar um projeto do zero.

TestContainers para desenvolvimento local

Para conseguirmos configurar a aplicação e usar TestContainers para auxiliar o desenvolvimento local, será preciso criar um novo arquivo. Vamos chamá-lo de TestContainersConfigurationApplication. Esse arquivo será utilizado para inicializar a aplicação e o TestContainers com o PostgreSQL. A implementação da classe java e uma explicação sobre a responsabilidade de cada método pode ser observada abaixo:

/**
* Test application to integrate with TestContainers for local development
*/
@Configuration
public class TestContainersConfigurationApplication {

public static void main(String[] args) {
SpringApplication.from(TestcontainersExampleApplication::main)
.with(TestContainersConfigurationApplication.class)
.run(args);
}

@Bean
@RestartScope
// springboot-devtools must be in test scope. This annotation will keep
// the container up and running even if the application restarts
@ServiceConnection
PostgreSQLContainer createPostgresSQLContainer() {
return new PostgreSQLContainer("postgres");
}
}

Como de costume no uso do Springboot, o método main() é utilizado para inicializar a aplicação principal TestcontainersExampleApplication. O que diferencia nesse caso é a configuração do TestContainers utilizando o método .with(), que no caso é a nossa classe de configuração TestContainerConfigurationApplication com o TestContainers.

No método createPostgresSQLContainer() é onde toda a magia acontece. O método, que utiliza a combinação das anotações @Bean, @RestartScope e @ServiceConnection é responsável por inicializar o container do PostgreSQL e garantir que ele esteja disponível antes da aplicação ser iniciada. Desta maneira, o serviço de banco de dados pode ser utilizado pela aplicação.

O recurso do Spring, a anotação @Bean é responsável por injetar o bean antes da aplicação inicializar, e que esse recurso pode ser injetado em qualquer outro bean na aplicação.

A anotação @RestartScope é uma anotação da dependência spring-boot-devtools que define que um bean se manterá inicializado, mesmo que a aplicação seja reiniciada. Isso permite que, mesmo se a aplicação for recarregada em tempo de desenvolvimento, o container vai se manter funcionando no mesmo estado, independentemente se a aplicação reiniciar.

Por fim, a anotação @ServiceConnection estabelece que Springboot possa estabelecer uma conexão automática com o serviço que está sendo executado no container. O ciclo de vida do container agora é gerenciado pelo Springboot, ou seja, o container é executado quando a aplicação inicializar e desligado quando a aplicação finalizar.

Ah! Mas eu utilizo imagens específicas no trabalho, que nunca existirão implementações dela no TestContainers. Não será possível utilizá-las, então?

Para esses cenários, o TestContainer disponibiliza a classe GenericContainer, que permite configurar e utilizar qualquer nome de imagem, incluindo a configuração de portas, redes, volumes, acesso a logs e até executar comandos dentro do container, similar ao que é permitido quando é utilizado o serviço Docker, como segue:

@Testcontainers
public abstract class AbstractIntegrationTest {

@Container
static GenericContainer container = new GenericContainer(DockerImageName.parse("myImage/myImage"))
.withEnv("MY_ENV", "value")
.withExposedPorts(9999)
.withNetworkAliases("myNetworkAlias");

@DynamicPropertySource
public static void myProperties(DynamicPropertyRegistry registry) {
registry.add("myimage.mail.host", container::getHost);
registry.add("myimage.mail.port", container::getFirstMappedPort);
}
}

No exemplo acima, a classe GenericContainer realiza as seguintes configurações:

  • Utiliza a a imagem myImage/myImage;
  • Define uma variável de ambiente chamada MY_ENV=value;
  • Expõe a porta 9999;
  • Define a rede myNetworkAlias.

Para mais detalhes sobre as possíveis configurações, você pode consultar a documentação da API.

No dia da publicação deste artigo, apenas os containers abaixo eram suportados nativamente pelo TestContainers:

  • CassandraContainer
  • CouchbaseContainer
  • ElasticsearchContainer
  • GenericContainer usando redis ou openzipkin/zipkin
  • JdbcDatabaseContainer
  • KafkaContainer
  • MongoDBContainer
  • MariaDBContainer
  • MSSQLServerContainer
  • MySQLContainer
  • Neo4jContainer
  • OracleContainer
  • PostgreSQLContainer
  • RabbitMQContainer
  • RedpandaContainer

Considerações finais

TestContainers possui uma API consistente que pode ajudar no gerenciamento de containers para seu ambiente local de desenvolvimento e para testes de integração, ajudando a isolar recursos externos dos seus testes, melhorando o código e levando a testes mais robustos e confiáveis. Desta forma, o desenvolvedor consegue ter um foco maior na implementação das regras de negócios.

Caso queira saber mais informações sobre TestContainers, visite o site oficial ou no repositório do GitHub.

--

--

Valdemar Jr
Livelo
Writer for

Tech Lead, apaixonado por tecnologia e early adopter curioso