Testes de integração mais rápidos com Docker e MongoDB em memória

A forma mais fácil de configurar o MongoDB para rodar em memória é utilizando um sistema de arquivos do tipo tmpfs.

Os sistemas de arquivo tmpfs não armazenam os dados em dispositivos de armazenamento permanentes, como HDs, pendrives, etc. Ao invés disso, manipulam os dados diretamente na memória RAM, ou seja, são voláteis. Por isso, muitas distribuições Unix os utilizam para diretório temporário /tmp ou até para compartilhamento de memória.

E podemos usar o MongoDB com sistema de arquivos tpmfs de forma bastante simples usando Docker:

docker run -p 27017:27017 --tmpfs=/data/db mongo:3.6.0

Como podemos ver, basta informar um diretório onde será montado o diretório em memória, neste exemplo /data/db o MongoDB já vai se entender com o resto.

Para rodar os testes localmente com esta instância do MongoDB, especialmente se você já tiver outra instância rodando, pode ser necessário mapear este banco em memória para uma porta alternativa. Isto pode ser feito facilmente alterando a porta externa (a primeira das duas) com a configuração -p 27018:27017. Feito isso, é só configurar os testes para utilizarem esta porta.

Abaixo, um exemplo com Docker Compose:

version: "3"
services:
  mongo-in-memory:
    image: mongo:3.6.0
    tmpfs: /data/db
    ports:
      - "27017:27017"

Alternativas a usar um banco em memória

Uma alternativa é utilizar algum tipo de biblioteca que simule o MongoDB, de modo a obter testes que executem rapidamente sem depender de ferramentas externas. Um exemplo é o mongomock para Python.

Esta opção tem algumas desvantagens, no entanto. Em especial, o comportamento de alguns índices pode não corresponder ao comportamento que seria obtido em uma instância real do MongoDB. Já passei por situações em que testes conseguiam violar índices únicos sem quebrar, e por isso não confio muito nesta solução.

Outra opção é simplesmente não utilizar uma biblioteca dessas e mockar todas as consultas ao MongoDB. Assim, teremos uma suíte cheia de testes unitários.

A maior desvantagem desta abordagem é que, ao mockar as chamadas ao banco, nossos testes acabam conhecendo a estrutura interna do código que eles testam. Eles terminam acoplados à implementação e se tornam frágeis, também conhecidos como testes anêmicos.

Rodando testes num pipeline de CI

Desta forma, também fica bem fácil rodar os testes de integração num pipeline de integração contínua. A seguir, vamos ver um exemplo no Azure DevOps.

Uma forma de usar o MongoDB nos testes usando o Azure DevOps, é adicionar uma task do tipo DockerCompose no job que roda os testes.

steps:
  - task: DockerCompose@0
    displayName: 'Turn-on MongoDB InMemory'
    inputs:
      containerregistrytype: 'Container Registry'
      dockerComposeFile: 'mongo-in-memory.yml'
      dockerComposeCommand: 'up --detach'
      workingDirectory: '$(WorkingDirectory)'

  # script para rodar os testes
  - script: |
            dotnet test $(ProjectName).sln --configuration Debug

O arquivo do Docker Compose mencionado no exemplo acima, mongo-in-memory.yml, é bastante simples também:

version: "3"
services:
  mongo-in-memory:
    image: mongo:3.6.0
    tmpfs: /data/db
    ports:
      - "27017:27017"

Assim, quando os testes rodarem, o MongoDB estará disponível. E o melhor: os testes vão rodar bem rápido.

Outras opções para compartilhar:
comments powered by Disqus