Última atualização:

Como conectar com mongodb no node.js com TDD

Nawarian
Nawarian javascript

Neste tutorial nós vamos ver como conectar sua aplicação Node.js com um banco de dados MongoDB. E como tem muita coisa acontecendo ao mesmo tempo, vamos trabalhar com TDL – aprendizado guiado por testes. Ou seja: vamos escrever testes para entender o que cada passo está fazendo.

Este passo a passo vai te permitir:

  • Subir um banco de dados MongoDB via Docker para testes;
  • Baixar e configurar o pacote mongoose;
  • Criar coleções no MongoDB via mongoose;
  • Inserir registros em coleções no MongoDB via mongoose; e
  • Listar registros em coleções no MongoDB via mongoose

Então vem comigo que a gente vai começar!

Conteúdos do post

Vídeo de apoio

Caso você esteja sem energia para ler o texto, ou tenha se perdido em algum momento, eu fiz um vídeo de apoio pra te auxiliar a entender tudo sobre como começar a trabalhar com MongoDB usando Mongoose em sua aplicação node.js.

Passo 1 – instalar o pacote mongoose

Eu estou rodando o node v16.17.0 mas os passos abaixo devem funcionar bem em versões superiores à 11 sem muitos problemas.

Vamos instalar o mongoose com npm usando a seguinte linha:

$ npm install mongoose --save

Caso você use yarn, o comando é o seguinte:

$ yarn add mongoose

O meu package.json ficou assim:

"dependencies": {
  "mongoose": "^6.6.2"
}

Passo 2 – instalar o pacote Jest para fazer TDD

Como nós queremos trabalhar com testes, vamos instalar o pacote Jest:

$ npm install --save-dev jest

Caso você utilize o Yarn, rode o seguinte comando:

$ yarn add --dev jest

Meu package.json ficou mais ou menos assim:

"devDependencies": {
  "jest": "^29.0.3"
}

Agora vamos adicionar a seguinte seção ao arquivo package.json:

"scripts": {
  "test": "jest"
}

Com isso nós podemos rodar o seguinte comando no terminal. A cada novo arquivo que criarmos e cada vez que salvarmos um arquivo, Jest irá rodar todos os testes novamente:

$ npm run test -- --watchAll

A sua tela deverá ficar mais ou menos assim:

Terminal rodando Jest e indicando que nenhum teste foi encontrado.
Terminal rodando Jest e indicando que nenhum teste foi encontrado.

Deixa esse terminal rodando o comando, a gente já já vai voltar a prestar atenção nele.

Passo 3 – Subindo um servidor MongoDB para testes

Para acompanhar nosso tutorial a gente precisa de um servidor MongoDB rodando. Como eu não quero gastar tempo e energia subindo um servidor na minha máquina local, eu vou utilizar a imagem docker oficial do MongoDB.

Esta imagem exige duas variáveis de ambiente definidas:

  • MONGO_INITDB_ROOT_USERNAME – o usuário padrão do banco de dados
  • MONGO_INITDB_ROOT_PASSWORD – a senha do usuário padrão

Então vamos criar um arquivo .env com o seguinte conteúdo:

MONGO_INITDB_ROOT_USERNAME=root
MONGO_INITDB_ROOT_PASSWORD=root

Com o arquivo acima criado, podemos iniciar o servidor docker com o seguinte comando:

$ docker run --rm -it --env-file .env -p27017:27017 mongo:latest

Deixa eu explicar o que comando acima está fazendo, item a item:

A opção --rm vai apagar o container docker assim que interrompermos sua execução. Dessa forma o container não fica em disco quando a gente terminar nosso teste.

A opção -it vai manter o shell conectado à saída do servidor, desta forma você consegue ver os logs do servidor em seu terminar. Como na imagem a seguir:

Terminal exibindo logs do servidor MongoDB
Terminal exibindo logs do servidor MongoDB

A opção --env-file .env vai usar o arquivo .env que nós criamos para definir as variáveis de ambiente que ficam presentes no container docker. Como nós definimos MONGO_INITDB_ROOT_USERNAME e MONGO_INITDB_ROOT_PASSWORD o container vai inicializar o servidor utilizando aqueles dados como usuário e senha padrão do banco de dados.

Por fim, -p27017:27017 vai mapear a porta 27017 do container à porta 27017 da máquina local. A porta 27017 é a porta padrão de conexão com o MongoDB. Ao fazer este mapeamento nós podemos acessar mongodb://localhost:27017 e o docker vai automaticamente nos rotear para o container.

O último argumento, mongo:latest, é o nome da imagem MongoDB no Docker Hub.

Passo 4 – vamos o esqueleto do nosso caso de teste

Agora é hora de colocar a mão na massa, vamos criar nosso caso de teste para testar nossa conexão com o MongoDB.

Adicione o seguinte esqueleto num arquivo mongodb.test.js:

// mongodb.test.js
const mongoose = require('mongoose')

test('Meu primeiro teste', () => {
  expect(true).toBeTruthy()
})

Ao salvar o arquivo acima, o terminal que nós deixamos rodando desde o passo 2 deverá ficar assim:

Terminal exibindo que o teste de exemplo passou
Terminal exibindo que o teste de exemplo passou

Nosso ambiente está pronto para experimentarmos com o Mongoose!

Passo 5 – testar a conexão com o MongoDB com mongoose e jest

Vamos começar por conectar com o MongoDB usando o mongoose. De acordo com a documentação oficial nós podemos usar o método mongoose.connect(), que retorna uma Promise.

Portanto o callback da função then() pode fazer nosso teste passar, e o callback da função catch() deverá fazer o teste falhar. Assim nós saberemos se a conexão está funcionando.

Vamos adicionar o seguinte caso de teste:

const MONGODB_DSN = 'mongodb://root:root@localhost:27017'

test('Conexão com MongoDB', async () => {
  const promise = mongoose.connect(MONGODB_DSN)
  
  await expect(promise).resolves.toBeInstanceOf(mongoose.Mongoose)

await mongoose.connection.close()
})

Ao salvar o arquivo com o teste acima, o Jest deverá imediatamente rodar os testes e acusar sucesso (caso os dados da conexão estejam corretos):

Terminal indicando que o teste 'Conexão com MongoDB' passou
Terminal indicando que o teste 'Conexão com MongoDB' passou

Aproveite e mude os valores da constante MONGODB_DSN para ter certeza de que o teste falha como deveria.

Agora vamos brincar com alguns schemas. Tá na hora de inserir uma entidade no nosso MongoDB.

Passo 6 – testar a inserção de entidades no MongoDB com mongoose e jest

É isso, vamos então criar uma coleção chamada artigos que possui os seguintes campos:

  • Título
  • URL

De acordo com a documentação oficial do Mongoose nós precisamos primeiro definir um objeto do tipo Schema, que mapeia o que acontece dentro do MongoDB para JavaScript. Depois nós precisamos criar um objeto do tipo Model para poder interagir com aquela coleção que definimos o Schema.

Ao inserir um objeto na coleção a gente sabe que tudo funcionou porque o mongoose vai adicionar um campo _id do tipo mongoose.Types.ObjectId.

Note que vamos definir Schema e Model fora do caso de teste, para que possamos reutilizar estas variáveis em outros casos de teste.

O caso de teste ficou assim:

const artigoSchema = new mongoose.Schema({
  titulo: String,
  url: String,
})

// 'Artigo' vai ser tratado como uma classe de agora em diante
const Artigo = mongoose.model('artigos', artigoSchema)

test('Inserção no MongoDB', async () => {
  // Nós já vimos que a linha abaixo conecta ao DB
  await mongoose.connect(MONGODB_DSN)

  // Cria o schema que mapeia mongodb para o JS
  const artigoSchema = new mongoose.Schema({
    titulo: String,
    url: String,
  })

  // 'Artigo' vai ser tratado como uma classe de agora em diante
  const Artigo = mongoose.model('artigos', artigoSchema)

  // Criamos uma instância de Artigo
  const tutorial_mongodb = new Artigo({
    titulo: 'Como conectar com mongodb no node.js com TDD',
    url: 'https://codamos.com.br/como-conectar-mongodb-nodejs-tdd',
  })

  // Salva o registro no MongoDB
  await tutorial_mongodb.save()

  // 'tutorial_mongodb' agora deverá ter um campo '_id'
  expect(tutorial_mongodb._id).toBeInstanceOf(mongoose.Types.ObjectId)

await mongoose.connection.close()
})

A tela do terminal do Jest ficou assim pra mim:

Terminal indicando que o teste 'Inserção no MongoDB' passou
Terminal indicando que o teste 'Inserção no MongoDB' passou

Passo 7 – testar a obtenção de documentos no MongoDB com mongoose e jest

Sempre que precisamos buscar um documento em nossa coleção, ou mesmo listar todos documentos, podemos utilizar o método Model.find() que deverá retornar o tipo Promise<Model[]>.

Então para o nosso teste podemos só verificar que a lista de documentos tem mais do que 0 elementos. Afinal o teste anterior já criou documentos no banco de dados.

Um teste de produção não deveria assumir que os testes anteriores passaram, e muito menos depender de estado global. Neste caso tudo bem trabalhar assim porque estamos utilizando testes para aprender.

O caso de testes ficou assim:

test('Listar todos documentos no MongoDB', async () => {
  await mongoose.connect(MONGODB_DSN)

  const artigoSchema = new mongoose.Schema({
    titulo: String,
    url: String,
  })

  const artigosEncontrados = await Artigo.find()

  expect(artigosEncontrados.length).toBeGreaterThan(0)

await mongoose.connection.close()
})

Jest deverá então acusar sucesso: nosso teste passou e temos uma lista de artigos na variável artigosEncontrados.

Terminal indicando que o teste 'Listar todos documentos no MongoDB' passou
Terminal indicando que o teste 'Listar todos documentos no MongoDB' passou

Você também pode experimentar fazer alguns console.log() dentro do escopo dos testes pra ver o que está lá dentro. O bacana de usar TDL é que temos um ambiente preparado para brincar e testar o que quisermos a qualquer momento, num ambiente controlado.

Código utilizado no artigo

Você pode verificar o código completo utilizado neste artigo neste GIST aqui.

Recomendação de outros testes para rodar

Você provavelmente já entendeu a ideia do negócio: cada funcionalidade que quiser testar, basta criar um novo caso de teste e ver como o framework se comporta.

Eu recomendo que você implemente por si os seguintes casos de teste:

  • Atualizar um documento MongoDB;
  • Buscar um documento pelo título;
  • Remover um documento por _id;
  • Receber a quantidade (count) de documentos que atendem a um filtro;
  • Listar documentos ordenados por url

Você vai ver que após escrever os testes acima, você já sabe tudo o que precisa pra começar a escrever uma aplicação completa usando Node.js, MongoDB e Mongoose.

Conclusão

Os testes que escrevemos aqui não devem ser utilizados em produção! São apenas testes que utilizamos para aprender a ferramenta. Assim que entender como a ferramenta funciona, recomendo remover estes testes e desenvolver sua aplicação de forma independente, com testes que façam sentido para a sua aplicação.

Não se esqueça de que ao interromper seu comando docker o banco de dados não vai mais funcionar. E ao rodar o mesmo comando novamente, seu banco de dados estará vazio porque usamos a opção --rm.

Se você gostou da forma como o artigo foi organizado, não deixa de compartilhar em suas redes sociais!

Comentários