Aniversário
Alura '2025

20% OFF

Falta pouco!

00

DIAS

00

HORAS

00

MIN

00

SEG

Alura > Cursos de Programação > Cursos de Node.JS > Conteúdos de Node.JS > Primeiras aulas do curso Node.js: testando API's REST e scripts assíncronos

Node.js: testando API's REST e scripts assíncronos

Testando scripts assíncronos com Node Teste Runner - Apresentação

Introdução ao Curso

Boas-vindas ao curso de testes assíncronos com Node.js. Meu nome é Tiago Bussola, sou instrutor na Escola de Programação e DevOps.

Audiodescrição: Tiago é um homem branco, com cabelo e barba escuros. Ele usa óculos de grau e veste uma camiseta branca. Ao fundo, há uma estante preenchida com livros e um armário branco iluminado por luzes azuis.

Objetivo do Curso

Este conteúdo é destinado a quem tem interesse em aprender a testar scripts assíncronos em Node.js e deseja aprofundar seus conhecimentos em testes de API REST.

Conteúdo do Curso

Neste curso, aprenderemos a utilizar o Node Test Runner para testar scripts assíncronos, além de como usar o Mocha, o Chai e o Sinon para aprimorar nossos testes. Exploraremos a configuração do Jest para projetos com módulos ECMAScript, o teste de APIs com rotas autenticadas e o uso do Copilot para agilizar a escrita dos testes. Também abordaremos a identificação de testes falsos e a configuração de variáveis de ambiente para testes utilizando o Jest Test Runner.

Aplicação Prática

Tudo isso será aplicado em um projeto de biblioteca que permite gerenciar livros cadastrados e autenticar usuários através de nossa API REST. Veremos também a criação e importação de livros no banco de dados por meio de scripts utilizando Node.js Streams.

Pré-requisitos

Para aproveitar melhor este curso, é recomendável que já tenhamos conhecimento prévio sobre como criar uma API REST utilizando Node.js e Express, manipular arquivos com os módulos internos ReadFile ou Node.js Streams, e que tenhamos concluído o curso anterior de testes unitários e de integração com Node.js, além de possuir noções básicas de teste.

Recursos Adicionais

Aproveitemos os recursos da plataforma. Além dos vídeos e das atividades, temos também o fórum e a comunidade do Discord para nos dar apoio. Vamos estudar?

Testando scripts assíncronos com Node Teste Runner - Entendendo o projeto inicial

Introdução ao Novo Projeto de API

Dando prosseguimento ao curso de testes que realizamos anteriormente, vamos testar um novo projeto com alguns scripts e interações assíncronas desta vez. O projeto é uma nova API, então, embora tenhamos o mesmo tema, que são livros e usuários autenticados, precisamos preparar todo o projeto e o ambiente inicial.

Verificação de Dependências e Configuração Inicial

Primeiro, vamos verificar o arquivo package.json para garantir que todas as dependências necessárias estão listadas. Caso não estejam instaladas, devemos rodar o comando npm install. Aqui está o conteúdo do package.json:

{
  "name": "4591-nodejs-testing",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js --watchAll",
    "test:coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage --watchAll",
    "test:ci": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
    "lint": "eslint ./src",
    "lint:fix": "eslint ./src --fix",
    "dev": "nodemon ./src/server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "bcrypt": "^5.1.1",
    "express": "^4.21.2",
    "jsonwebtoken": "^9.0.2",
    "mongoose": "^8.10.2"
  },
  "devDependencies": {
    "@faker-js/faker": "^9.6.0",
    "chai": "^5.2.0",
    "jest": "^29.7.0",
    "mocha": "^11.1.0",
    "sinon": "^20.0.0",
    "supertest": "^7.1.0"
  }
}

Com as dependências verificadas, podemos instalar tudo com o seguinte comando:

npm install

Configuração do Banco de Dados com Docker

No docker-compose, deixamos um banco de dados MongoDB preparado apenas para realizarmos os testes. Portanto, utilizamos o comando docker-compose up -d. Aqui está a configuração do docker-compose.yml:

version: '3'

services:
  mongo:
    image: mongo
    volumes:
      - ./data:/data/db
    ports:
      - '27017:27017'

Para iniciar o MongoDB, executamos:

docker compose up -d

Configuração de Variáveis de Ambiente

No arquivo .env, temos algumas variáveis que utilizaremos posteriormente. Não agora, mas temos a variável APP para definir se nosso aplicativo é de desenvolvimento ou de produção, uma palavra secreta para o nosso JWT, e duas URLs do MongoDB: uma para teste e outra para produção.

MONGODB_URI=mongodb://0.0.0.0:27017/books
MONGODB_TEST_URI=mongodb://0.0.0.0:27017/books_test
JWT_SECRET=batataDoce
APP="dev"

Geração de Dados para o Banco de Dados

Como mencionado, temos uma API muito similar à do curso anterior de testes unitários e testes de APIs. A diferença aqui é que agora estamos lidando com tabelas um pouco diferentes e utilizaremos alguns scripts para popular nosso banco de dados. Como o banco está vazio, geraremos alguns livros aleatórios e os importaremos no banco.

Para simular que estamos gerando esse arquivo para enviar a nós mesmos e inserir no banco de dados, utilizamos o script generateBooks. Ele cria uma stream de escrita e percorre até o number of books, onde definimos um número de livros e o arquivo que queremos escrever. Aqui está o código do script:

import fs from "fs";
import { faker } from "@faker-js/faker";

export async function generateBooksToNDJSON(filePath, numberOfBooks) {
  const writeStream = fs.createWriteStream(filePath);

  for (let i = 0; i < numberOfBooks; i++) {
    const book = {
      title: faker.lorem.words(3),
      author: faker.person.fullName(),
      ISBN: faker.string.numeric(10),
    };
    writeStream.write(JSON.stringify(book) + "\n");
  }

  writeStream.end();
}

generateBooksToNDJSON("books.ndjson", 100000);

Esse script monta um objeto com três propriedades: o título, que é formado por três palavras aleatórias do lorem ipsum; o autor, escolhido de uma lista extensa do faker; e o ISBN, que é uma string formada por dez números aleatórios gerados pelo faker. Em seguida, escrevemos cada um dos livros no arquivo, transformando o objeto em uma string e quebrando a linha no final. Dessa forma, conseguimos montar um arquivo no formato NDJson.

Abaixo, fizemos a chamada da função para criar um arquivo chamado books.ndjson, gerando 100 mil livros. Vamos limpar o terminal. Neste momento, o banco de dados não precisa estar ativo, então não há problema. Tudo que fazemos é rodar o script com o comando:

node scripts/generateBooks.js

O processo demora menos de um segundo. Ao abrir o arquivo, observamos que o título é formado por três palavras, como "Jimmy Johnson", e um número de dez caracteres, todos aleatórios. Tudo está em formato de string no nosso esquema. Ao pressionar control enter, verificamos que temos 100 mil registros criados.

Preparação para Testes Futuros

Agora, precisamos trabalhar em alguns testes. Todos esses livros foram gerados de forma assíncrona. No próximo vídeo, utilizaremos o Node Test Runner, o Test Runner nativo do Node, para testar se estamos gerando o número correto de livros, se os livros estão no formato esperado, entre outros aspectos. Nos vemos no próximo vídeo.

Testando scripts assíncronos com Node Teste Runner - Criando testes nativos

Introdução ao Teste de Scripts

Agora que validamos que nosso script está funcionando conforme o esperado e gerou a quantidade de linhas com registros que solicitamos, vamos escrever o teste dele usando o testRunner nativo do Node.

Um script não é diferente de rotas ou funções. Na verdade, ele é uma função que executa o que colocamos ali dentro, e precisamos validar se isso realmente está de acordo com o que esperamos. Precisamos verificar se ele está gerando um livro válido, se o formato do arquivo está correto e se a quantidade de linhas é exatamente a que passamos. Assim como fazemos um teste unitário de uma função, devemos fazer o teste unitário também para nossos scripts, para garantir que eles funcionem da maneira esperada.

Configuração do Ambiente de Teste

Vamos, então, à pasta raiz do projeto. Criaremos uma pasta chamada "testes" e, dentro dela, uma pasta chamada "scripts". Em seguida, criaremos um novo arquivo chamado testgeneratebooks.js. Por enquanto, não utilizaremos a nomenclatura de spec nem a nomenclatura de .teste, mas deixaremos isso para depois.

Primeiro, vamos colar alguns imports que já deixaremos prontos para não perdermos muito tempo digitando. Estamos importando a função test do Node test, do test runner nativo, o assert, o fs do fs/promises, o path e o url para normalizar nossos caminhos. Também importaremos a função do nosso script para gerar os livros.

import { test } from "node:test";
import assert from "node:assert";
import fs from "fs/promises";
import path from "path";
import { fileURLToPath } from "url";
import { generateBooksToNDJSON } from "../../scripts/generateBooks.js";

Normalização de Caminhos e Limpeza de Arquivos

Como estamos trabalhando dentro da pasta de teste, não queremos que, ao executar nosso script, o arquivo seja criado na raiz do projeto. O Node cria o arquivo no local de onde você executou. Portanto, se executarmos da raiz, ele criará o arquivo na raiz. Queremos que o arquivo de livros seja criado dentro da pasta "scripts", pois é lá que estamos trabalhando agora, para não poluir nosso ambiente.

Tudo relacionado ao teste deve ser mantido em um local específico. Para isso, vamos realizar uma normalização. Assim, const __dirname recebe path.dirname, passando o fileURLToPath e o import.meta.url.

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Se estivermos acostumados a usar outros test runners, como no curso de testes unitários em que utilizamos o Jest, temos funções como before, beforeAll, afterAll, que executam antes e depois dos testes. No ambiente nativo, ainda não dispomos dessas funções. Portanto, desejamos que, sempre que nosso script de teste for executado, ele limpe o arquivo de livros no início e no final, removendo esses arquivos para garantir que cada teste seja independente.

Como não temos as funções afterAll e beforeAll, vamos criar uma função para limpar o arquivo e chamá-la em cada um dos testes, conforme necessário. Vamos criar uma função assíncrona chamada cleanupFile, que receberá como parâmetro o caminho do arquivo, filePath.

async function cleanupFile(filePath) {
    try {
        await fs.unlink(filePath);
    } catch (error) {
        if (error.code !== 'ENOENT') throw error;
    }
}

Criação e Execução do Teste

Agora, vamos criar um próximo teste, semelhante ao que vimos no Jest. Podemos definir um nome para o teste e o que ele executará. Neste caso, o teste deve gerar o número correto de livros. Passaremos uma função síncrona como callback, que será executada no teste.

test('Deve gerar o número correto de livros', async () => {
    const testFilePath = path.join(__dirname, "test-books.ndjson");
    const numberOfBooks = 100;

    await cleanupFile(testFilePath);
    await generateBooksToNDJSON(testFilePath, numberOfBooks);

    const content = await fs.readFile(testFilePath, "utf-8");
    const lines = content.trim().split("\n");

    assert.strictEqual(lines.length, numberOfBooks, `Deveria gerar ${numberOfBooks} livros`);

    // await cleanupFile(testFilePath);
});

Dentro do teste, definiremos const testFilePath como path.join, passando __dirname e test-books.ndjson, que será o nome do arquivo de livros utilizado. Assim, ele será gerado dentro da pasta "script" e se chamará test-books.ndjson. Também definiremos const numberOfBooks para especificar o número de livros, facilitando alterações futuras.

Iniciaremos chamando cleanupFile para garantir que o arquivo test-books não exista antes de criá-lo. Em seguida, chamaremos await generateBooksToNDJSON, passando o caminho do arquivo de teste e o número de livros a serem inseridos.

Após isso, contaremos o número de linhas. Utilizaremos const content = await fs.readFile, lendo o arquivo test-books.ndjson em utf8. Em seguida, criaremos const lines, que receberá o conteúdo da leitura, removendo espaçamentos e dividindo a string por \n. Isso nos permitirá contar quantas linhas existem no arquivo e verificar se o número de livros está correto.

Faremos uma asserção com assert.strictEqual, passando o valor atual lines.length, o valor esperado numberOfBooks e uma mensagem opcional para facilitar a identificação de erros no terminal. A mensagem será "Deveria gerar" seguido do número de livros.

Execução do Teste e Considerações Finais

No final, como teremos outros testes, deletaremos o arquivo. No entanto, na primeira execução, deixaremos essa linha comentada para verificar se o arquivo foi gerado corretamente.

Para executar, abriremos o terminal, fecharemos o explorador de arquivos e executaremos node testes-scripts-testGenerateBooks.js. Ao dar enter, o teste falhou, indicando que não encontrou o arquivo, apesar de ter gerado um arquivo com 100 registros. Isso ocorreu devido a uma race condition, que resolveremos no próximo vídeo.

Sobre o curso Node.js: testando API's REST e scripts assíncronos

O curso Node.js: testando API's REST e scripts assíncronos possui 200 minutos de vídeos, em um total de 51 atividades. Gostou? Conheça nossos outros cursos de Node.JS em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Escolha a duração
do seu plano

Conheça os Planos para Empresas