Facilitando seus testes de integração em Java com TestContainers
Tags: java, docker, testing
Published: 2021-04-07 11:56:18

Para maioria de nós desenvolvedores, escrever testes unitários é algo comum em nosso dia a dia. Seja para garantir a qualidade de nossos trechos de código, ou, para os menos exigentes, passar na cobertura mínima exigida pela esteira de CI/CD, desenvolver testes unitários é uma tarefa simples, mesmo para desenvolvedores com pouca experiência. 

Mas quando falamos sobre testes de integração há uma certa complexidade, principalmente quando nossas aplicações possuem **dependências externas**, como, por exemplo, salvar registros em bancos de dados, enviar mensagens para um broker de mensageria, etc.

A biblioteca [TestContainers](https://www.testcontainers.org/) nos ajuda a resolver estas **dependências externas**. Com a possibilidade de executar imagens docker durante a execução de nossos testes, podemos, por exemplo, subir uma instância de RabbitMq para que a nossa aplicação se conecte, e por fim a instância é finalizada após o término da execução dos testes.

Neste artigo, veremos um exemplo de uma aplicação Spring que se conecta a um MongoDB para salvar registros através de um endpoint, e utilizaremos TestContainers para escrever nossos testes integrados. 

É necessário ter o docker instalado na máquina para seguir os passos deste artigo.

Passaremos pelos principais pontos para utilização da biblioteca, o código pronto está disponível em: {% github Jonathas-garcia/poc-testcontainer no-readme %}

## Criação do projeto 

Para começar, crie uma nova aplicação Spring com as seguintes dependências:
- Spring Web, para criar um endpoint REST.
- Spring Data MongoDB, para a comunicação com o banco de dados.

Siga o exemplo do repositório e crie suas classes de model, repository, service e controller.

Adicione a dependência do TestContainer no **pom.xml**.
{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=pom.xml %}

No pacote *test* crie a classe **MongoDbContainer**.
{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=MongoDbContainer.java%}

Importante ressaltar neste ponto que poderíamos utilizar diretamente a classe MongoDBContainer, provido pelo TestContainer, conforme a [documentação oficial](https://www.testcontainers.org/modules/databases/mongodb/), porém o intuito é também demonstrar a classe **GenericContainer** pois com ela podemos referenciar a qualquer imagem pública, que neste caso referenciamos `mongo` na sua versão `latest`.

Também no pacote *test*, crie a classe responsável por registrar e inicializar as properties para conexão com o banco.

{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=MongoDbContextInitializer.java %}

E então, crie sua classe de teste, executando a instância do MongoDB antes da execução dos testes no @beforeAll, e executando as chamadas HTTP's dentro do caso de teste.

{% gist https://gist.github.com/Jonathas-garcia/693f0dfdf94097351ef421a09b505260 file=PocTestcontainerApplicationTests.java %}

A chamada POST chamou o método do nosso controller que salva a mensagem no MongoDB, e realizando a chamada GET, garantimos que essa Mensagem foi salva, pois foi retornada.

## Executando 

Na pasta do projeto execute os testes.
```
./mvnw test
```

Biblioteca irá chamar o docker que executará o contêiner com a imagem do MongoDB.

![Container mongo sendo executado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j7iukow6n8ydj5eq2a4h.png)

Contêiner do TestContainer e do Mongo são iniciados no docker local.

![Containers executando](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hs7yxwwry0sptlsk1aql.png)

Aplicação é iniciada e abre conexão com a instância do banco de dados.

![Aplicação abre conexão com o banco](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bu60n17pvjsh8ppdr2vn.png)

Testes são executados com sucesso e os Contêineres são finalizados e removidos para evitar sujeira no disco.

![Testes executados com sucesso](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/diu78zlycxqpr4qxy0so.png)


## Conclusão

Vimos que a biblioteca facilita nossos testes de integração, fornecendo instâncias de qualquer aplicação que possa ser executada em um contêiner docker. TestContainers ainda possui suporte para docker compose, trazendo mais possibilidades para seus testes.