Testes integrados com Docker-compose/Flyway/Fixture-factory

Glendon M. Leitão
Tech@Grupo ZAP
Published in
5 min readJan 4, 2017

Teste unitários são legais, por quê? São baratos (fáceis de fazer e manter) e são rápidos — @sabrinajn em seu post fala, entre outras coisas, sobre os custos de realização de testes.

"Porém! Ai porém!"

Quando os componentes individuais se integram, lidam com sistemas externos (DBs, APIs externas, etc), tudo pode dar errado e o barco afundar.

Mas como podemos evitar esse tipo de surpresa? Uma opção é construir/melhorar nossos testes de integração. Reforçando, é uma opção, pois existe o trade-off, que eu acredito que vale a pena.

Para este post vou categorizar a realização de testes de integração em duas linhas conforme o tipo de sistema externo que iremos consumir:

1 — Com Banco de Dados

2 — Com APIs Externas

Abordarei apenas ferramentas para teste de integração com Banco de Dados — mais especificamente com o PostgreSQL, mas facilmente aplicável a outros bancos. Apresentarei uma das estratégias e ferramentas que utilizamos no VivaReal que diminuem bastante nossa dor/custo/tempo para a realização desses testes.

Utilizaremos as seguinte ferramentas:

Docker-compose

O que ele faz?

Segundo a sua própria descrição “É uma ferramenta para definição e execução de aplicações Docker de múltiplos containers. Com Compose, você usa um arquivo Compose para configurar os seus serviços. Então, usando um único comando, você cria e inicia todos os serviços a partir da sua configuração.” Em resumo, você tem várias imagens Docker (com bancos de dados configurados, outros sistemas, mock-servers, etc) e com um comando você inicia todos eles.

Por que Docker-compose?

No nosso contexto de teste de integração, a idéia é você executar esses containers, usar eles como um sandbox, no fim matar tudo e na próxima execução o estado deles estará exatamente como você precisa.

Flywaydb

O que ele faz?

É uma ferramenta para migração de banco de dados. Você consegue versionar a sua estrutura de banco de dados. O melhor: você ganha nos testes e na governabilidade da sua estrutura.

Ps: Serei bem focado no teste de integração, porém recomendo fortemente explorar um pouco mais sobre o Flyway para obter as vantagens que ele oferece com o versionamento da estrutura do seu banco.

Por que Flywaydb?

Flywaydb + Docker + Docker-compose = um banco na versão exata que você precisa para os seus testes.

Fixture-factory

O que ele faz?

Ajuda a criar e organizar objetos fakes.

Por que Fixture-factory?

Além de criar os seus objetos fakes, o que deixa seu código de teste mais simples (para entender e manter), o Fixture-factory tem um mecanismo chamado Processor, que ajuda a criar uma lógica após a criação desses objetos fakes. Assim vamos criar os objetos fake e popular nosso banco de dados. Logo:
Fixture-factory + Flywaydb + Docker + Docker-compose = um banco no estado que você precisa.

SHOW (me the code) TIME

Fiz um projeto muito simples de exemplo focado no teste de integração com o banco de dados (basicamente um CRUD com um método main), utilizando a stack Docker-compose/Flyway/Fixture-factory.

O código pode ser encontrado neste repositório.

Vamos pegar a classe ShipDaoIT, que realiza o teste integrado da classe ShipDao.

Para executar o teste na raiz do projeto:

1 — Executar o docker-compose (é necessário ter Docker e o Docker-compose instalados):

docker-compose up -d

2 — Executar os testes:

gradle test

Como foi feito?

O primeiro passo foi preparar um container com o nosso banco de dados. Na raiz do projeto temos o arquivo docker-compose.yml:

O que fiz aqui?

Declarei um container que subirá com postgres e estará disponível na porta 5432.

Na linha 5 utilizei a imagem docker do postgres disponível em: https://hub.docker.com/_/postgres/

A partir da linha 8 declarei um novo banco chamado integrated com um usuário e senha padrão.

E agora?

Com um banco completamente limpo rodando em um container, preciso rodar os scripts para criar as tabelas e o que mais for necessário antes de executar os testes.

O segundo passo é configurar o Flyway. Temos que criar os arquivos de migrations (que serão lidos para criar/popular a estrutura do banco), na pasta db.migration.

Obrigatoriamente o nome do arquivo deve ter a seguinte estrutura:

VN__qualquernome.sql

Onde:

N = versã0, deve ser sequencial. Ex.: 1, 2, 3.

__ = atentar que são dois underscores.

qualquernome = tá claro :)

Nosso script é muito simples:

O terceiro passo é garantir que antes da execução dos testes iremos executar o Flyway para realizar a migração dos dados.

No exemplo acima antes da execução da classe de teste ele limpa o banco e migra tudo que está na pasta db.migration (ele lê por default essa pasta, mas pode ser configurado) na ordem conforme a versão (V1, V2, etc).

Neste ponto, você tem um banco de dados para teste, na versão que você precisa. E o estado do banco? Não precisaremos de uns dados para fazer alguns tipos de testes? Normalmente sim. Podemos fazer de duas formas:

  • Dados mais constantes, que serão usados em prod/qa/dev - por exemplo estados brasileiros - podemos popular em um arquivo de migration do Flyway, basta adicionar instruções DML nesses arquivos.
  • Dados inconstantes - por exemplo pessoas, documentos, transações — estes podem ser populados usando Fixture-factory.

Vamos focar nos dados inconstantes e ver como o Fixture-factory funciona.

Populando o seu banco com objetos fakes

Criei um template para objetos fakes, no caso acima, objetos do tipo Ship, populando com uma regex o atributo name. Esse é só um exemplo extremamente simples, para conhecer todas as possibilidades visite a página do projeto Fixture-factory.

Agora que tenho o objeto fake criado, eu preciso que ele esteja no banco de dados. Como posso gravar no banco esse objeto fake para utilizar em um caso específico de teste de forma simples? Utilizando um Processor.

O nosso processor ShipProcessor é muito simples: recebe um objeto e grava no banco.

A classe ShipProcessor tem um único atributo entityManager recebido no construtor e implementa a interface br.com.six2six.fixturefactory.processor.Processor para conseguir processar o objetos criado. Como ele é criado e chamado?

Na linha 12, criei 10 Ships que usaram o shipProcessor para serem populados no banco. Simples :)

Conclusão

Baseado na stack proposta "docker-compose/Flyway/Fixture-factory" conseguimos configurar um ambiente para testes integrados de forma simples.

Você pode explorar mais como usar docker e docker-compose em seus testes integrados para, por exemplo, criar mocks de APIs externas.

That’s all folks!

--

--