Leodario.com

Leodario.com – Tudo sobre Tecnologia

Aula 72 – API JavaScript, Node.js e Express – Jest Setup – Testing framework configuration

Imagem destacada da aula de API

Introdução (3 min)

Imagine que você é um chef de cozinha renomado, prestes a inaugurar um restaurante de alta gastronomia. Antes de abrir as portas para o público, você não apenas prepara cada prato, mas também meticulosamente testa cada ingrediente, cada tempero e cada etapa da receita para garantir a perfeição. Você verifica se o forno está na temperatura correta, se os utensílios estão limpos e se cada componente da cozinha está pronto para a operação.

Este é exatamente o papel da configuração do Jest no desenvolvimento de APIs modernas. É o estágio primordial onde montamos nosso laboratório de testes, ajustamos os equipamentos e definimos as regras do jogo antes de rodar os experimentos. Para APIs, isso é vital porque garante a qualidade, a confiabilidade e a manutenibilidade do seu serviço. Uma API com testes bem configurados é uma API que respira confiança e que possibilita que novas funcionalidades sejam adicionadas sem medo de quebrar o que já funciona.

Nesta aula, você vai desvendar como instalar e configurar o Jest, o framework de testes mais popular para JavaScript, em seu projeto Node.js. Aprenderemos a otimizar essa configuração para ambientes de produção e entender as melhores práticas que se alinham com os padrões enterprise. Prepare-se para desenvolver uma base sólida para a testabilidade de suas APIs.

No vasto ecossistema Node.js e Express, onde a velocidade de desenvolvimento é uma constante, a capacidade de testar seu código de forma eficiente é um diferencial. O Jest se integra harmoniosamente, viabilizando a escrita de testes robustos para suas rotas, middlewares e lógica de negócio, assegurando que seu backend permaneça íntegro e performático.

Conceito Fundamental (7 min)

O Jest é um framework de testes JavaScript com “zero configuração” na teoria, mas que oferece um universo de possibilidades de customização para cenários mais complexos e profissionais. Sua função principal é ser um executor de testes (test runner) que encontra, executa e reporta os resultados dos seus testes. Ele vem “completo”, ou seja, inclui o test runner, o framework de asserções e até mesmo um mock de DOM, o que o torna uma solução abrangente para testar qualquer código JavaScript.

No cerne do Jest estão os conceitos de testes (os casos que você quer verificar), expectativas (o resultado esperado de um teste) e matchers (métodos que permitem comparar um valor com uma expectativa). Por exemplo, expect(sum(1, 2)).toBe(3) é uma expectativa que usa o matcher toBe para verificar se a soma de 1 e 2 é exatamente 3. Um conjunto de testes para uma funcionalidade específica é conhecido como suíte de testes.

Em um ambiente de produção de APIs, a configuração do Jest é significativa para diversos casos de uso:

    • Testes Unitários: Garante que funções e módulos individuais (como um validador de dados ou um utilitário de formatação) funcionem como esperado, isoladamente.
    • Testes de Integração: Verifica a interação entre diferentes partes do sistema, por exemplo, se uma rota do Express se comunica corretamente com um serviço ou um banco de dados simulado.
    • Testes de Snapshot: É valioso para garantir que a saída de componentes complexos ou objetos de dados (como a resposta de uma API) não mude inesperadamente, capturando um “instantâneo” da saída e comparando-o em execuções futuras.
    • Cobertura de Código: O Jest pode relatar a porcentagem do seu código que está sendo testada, um indicador primordial da qualidade dos seus testes.

A integração do Jest com outras tecnologias é notavelmente fluida. Ele funciona perfeitamente com projetos Node.js, Express, TypeScript (com ts-jest), Babel (com babel-jest) e até mesmo com frameworks de frontend, se você estiver construindo uma solução full-stack. Essa flexibilidade habilita equipes a manterem um ambiente de testes consistente em toda a pilha tecnológica.

As vantagens de uma boa configuração do Jest são muitas:

    • Velocidade: Desenvolvido para ser rápido, executando testes em paralelo.
    • Facilidade de Uso: Sintaxe clara e legível, com pouca configuração inicial.
    • Ecossistema Rico: Vem com mocks, cobertura de código e um poderoso sistema de asserções.
    • Manutenção Aprimorada: Testes bem escritos e configurados facilitam refatorações e o desenvolvimento de novas funcionalidades com menor risco.

Por outro lado, algumas desvantagens podem surgir:

    • Configuração Avançada: Para cenários muito específicos (como módulos ES ou TypeScript complexos), a configuração pode exigir um mergulho mais profundo.
    • Recursos: Pode consumir mais memória para grandes suítes de testes ou recursos de watch.

No contexto da HostGator Plano M, onde o ambiente de execução é Node.js padrão, o Jest roda perfeitamente em seu ambiente de desenvolvimento local. A configuração que você implementa localmente é a que garante a qualidade do código que será eventualmente implantado no servidor.

Implementação Prática (10 min)

Vamos agora construir a configuração de um projeto Node.js/Express para utilizar o Jest, seguindo as melhores práticas empresariais e garantindo a compatibilidade.

Primeiro, crie um novo projeto Node.js e instale o Jest.


Crie um diretório para o seu projeto e navegue até ele

📚 Informações da Aula

Curso: API Completo - Node.js & Express

Tempo estimado: 25 minutos

Pré-requisitos: JavaScript básico

mkdir jest-api-setup cd jest-api-setup

Inicialize um novo projeto Node.js com configurações padrão

npm init -y

Instale o Jest como uma dependência de desenvolvimento

npm install --save-dev jest

Agora, vamos adicionar um script de teste ao seu arquivo package.json. Isso permite que você execute seus testes de forma fácil e padronizada.

Abra o arquivo package.json e adicione a linha "test": "jest" dentro do objeto "scripts".

// package.json
{
  "name": "jest-api-setup",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest", // Adicione esta linha
    "test:watch": "jest --watch" // Opcional: para rodar testes ao salvar arquivos
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^29.7.0"
  }
}

  • "test": "jest": Define o comando npm test para executar o Jest.
  • "test:watch": "jest --watch": Um script opcional que possibilita o Jest a monitorar arquivos e rodar testes automaticamente sempre que houver mudanças.

Para configurações mais avançadas e específicas para a HostGator Plano M (onde o Node.js é o ambiente), é essencial criar um arquivo jest.config.js na raiz do seu projeto. Isso facilita o gerenciamento de configurações complexas.

Crie um arquivo chamado jest.config.js na raiz do seu projeto:

// jest.config.js
/* @type {import('jest').Config}
 /
const config = {
  // Define o ambiente de teste como Node.js.
  // Isso é crucial para testar APIs de backend, pois não precisamos de um DOM de navegador.
  testEnvironment: 'node',

// Especifica os diretórios onde o Jest deve procurar por arquivos de teste. // '__tests__' é uma convenção comum, mas 'src' ou 'test' também são válidos. // 'roots' viabiliza que o Jest saiba onde começar a busca por testes. roots: [ "/src", // Busca testes dentro da pasta 'src' "/__tests__" // Busca testes dentro da pasta '__tests__' ],

// Padrões de nomes de arquivos que o Jest deve considerar como testes. // Isso permite flexibilidade em como você nomeia seus arquivos de teste. testMatch: [ "/__tests__//.test.js", // Arquivos .test.js dentro de '__tests__' "*/?(.)+(spec|test).js", // Arquivos com .spec.js ou .test.js em qualquer lugar // Se estiver usando TypeScript, adicione .ts e .tsx aqui // "*/?(.)+(spec|test).ts?(x)" ],

// Expressões regulares que casam com arquivos que o Jest deve ignorar. // Isso é significativo para evitar testar arquivos de build, node_modules, etc. testPathIgnorePatterns: [ "/node_modules/", "/dist/" // Ignora a pasta de build ],

// Habilita a coleta de cobertura de código. // Primordial para garantir que uma boa parte do seu código está sendo testada. collectCoverage: true,

// Diretório onde os relatórios de cobertura serão gerados. coverageDirectory: "coverage",

// Onde o Jest deve procurar por arquivos para coletar a cobertura. // Isso habilita a visualização da cobertura para todo o seu código-fonte. collectCoverageFrom: [ "src/*/.js", // Inclui todos os arquivos .js dentro da pasta 'src' "!src/index.js", // Exclui arquivos específicos, se necessário "!src/config/.js" // Exclui arquivos de configuração, por exemplo ],

// Define um limite mínimo para a cobertura de código, o que é uma prática enterprise para manter a qualidade. // Se a cobertura cair abaixo desses valores, os testes falharão. coverageThreshold: { global: { branches: 80, // % mínimo de branches (ramificações) cobertas functions: 80, // % mínimo de funções cobertas lines: 80, // % mínimo de linhas cobertas statements: 80 // % mínimo de statements cobertos } },

// Mapeia módulos para que o Jest possa resolver aliases, se você os usar em seu projeto. // Exemplo: se você tem '@utils' mapeando para './src/utils', configure aqui. // Isso é relevante para projetos maiores com estrutura de módulos bem definida. moduleNameMapper: { "^@utils/(.)$": "/src/utils/$1" },

// SetupFiles: Array de módulos para rodar antes de cada teste. // Valioso para configurar variáveis de ambiente ou mocks globais. // Por exemplo, para carregar configurações de ambiente para testes. // setupFiles: ["/jest.setup.js"] };

module.exports = config;

Configurações Específicas para HostGator Plano M:

Para a HostGator (ou qualquer ambiente de produção Node.js padrão), o importante é que seu código seja Node.js válido. As configurações do Jest acima são para o ambiente de desenvolvimento e não afetam diretamente o deploy. No entanto, garantir que seu código-fonte (src/*/.js) seja compatível com a versão do Node.js da HostGator é fundamental.

Error Handling Otimizado:

Uma boa configuração do Jest também implica em um “error handling” para os próprios testes:

  • Se um teste falhar, a mensagem de erro deve ser clara e indicar o problema.
  • O coverageThreshold garante que a qualidade do teste seja mantida, falhando o build se a cobertura cair.

Exemplo de Código para Teste:

Vamos gerar um arquivo de exemplo para testar.
Crie um diretório src e dentro dele, um arquivo sum.js:

// src/sum.js
/* Adiciona dois números e retorna o resultado.
  @param {number} a O primeiro número.
  @param {number} b O segundo número.
  @returns {number} A soma dos dois números.
 /
function sum(a, b) {
  if (typeof a !== 'number' || typeof b !== 'number') {
    // Isso é um exemplo de validação de entrada robusta.
    // Em uma API, você validaria os parâmetros de entrada.
    // E Jest te permite testar esse cenário de erro.
    throw new Error('Ambos os parâmetros devem ser números.');
  }
  return a + b;
}

module.exports = sum;

Agora, crie um diretório __tests__ e dentro dele, um arquivo sum.test.js:

// __tests__/sum.test.js
const sum = require('../src/sum'); // Importa a função a ser testada

describe('Função de Soma', () => { // Uma suíte de testes para a função sum test('deve somar dois números positivos corretamente', () => { // Um teste individual // expect(sum(1, 2)).toBe(3); // Expectativa e matcher const result = sum(1, 2); expect(result).toBe(3); // Verifica se o resultado é 3 });

test('deve somar um número positivo e um negativo corretamente', () => { expect(sum(5, -3)).toBe(2); });

test('deve lançar um erro se os parâmetros não forem números', () => { // Para testar se uma função lança um erro, você envolve a chamada em uma função e usa toThrow expect(() => sum(1, '2')).toThrow('Ambos os parâmetros devem ser números.'); }); });

  • describe: Agrupa testes relacionados em uma suíte.
  • test (ou it): Define um caso de teste individual.
  • expect: Começa uma asserção, envolvendo o valor que você quer testar.
  • toBe: Um matcher que verifica a igualdade de valor.
  • toThrow: Um matcher que verifica se uma função lança um erro.

Para rodar seus testes, simplesmente execute:

npm test

Você verá a saída do Jest, indicando os testes que passaram e a cobertura de código (se collectCoverage: true estiver configurado).

Exercício Hands-On (5 min)

Agora é a sua vez de consolidar este conhecimento!

Desafio Prático

Desafio: Em um novo projeto Node.js (ou no mesmo que acabamos de gerar), configure o Jest para:

    • Buscar arquivos de teste apenas em uma pasta tests/unit e tests/integration.
    • Garantir que todos os arquivos .js dentro da pasta src/services e src/controllers sejam incluídos na cobertura de código.
    • Estabelecer um limite de cobertura global de 90% para linhas e 85% para funções.
    • Desenvolva uma função simples multiply(a, b) em src/services/math.js e crie um teste correspondente em tests/unit/math.test.js que verifique se ela multiplica dois números corretamente e se lança erro para entradas inválidas.

Solução Detalhada Passo a Passo

  • Crie a Estrutura do Projeto:
    mkdir jest-challenge
    cd jest-challenge
    npm init -y
    npm install --save-dev jest
    mkdir -p src/services src/controllers tests/unit tests/integration

  • Crie o arquivo package.json (se não existir, o npm init -y já o faz) e adicione o script de teste:
    // package.json
    {
      "name": "jest-challenge",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "test": "jest"
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "devDependencies": {
        "jest": "^29.7.0"
      }
    }

  • Crie o arquivo jest.config.js na raiz do projeto com as configurações solicitadas:
    // jest.config.js
    /* @type {import('jest').Config}
     /
    const config = {
      testEnvironment: 'node',
      roots: [
        "/tests/unit",
        "/tests/integration"
      ],
      testMatch: [
        "/?(.)+(spec|test).js"
      ],
      collectCoverage: true,
      coverageDirectory: "coverage",
      collectCoverageFrom: [
        "src/services/*/.js",
        "src/controllers/*/.js"
      ],
      coverageThreshold: {
        global: {
          lines: 90,
          functions: 85
          // branches e statements podem ser adicionados se desejar
        }
      }
    };

module.exports = config;

  • Implemente a função multiply:

Crie src/services/math.js:

    // src/services/math.js
    function multiply(a, b) {
      if (typeof a !== 'number' || typeof b !== 'number') {
        throw new Error('Os argumentos devem ser números.');
      }
      return a * b;
    }

module.exports = multiply;

  • Escreva o teste para multiply:

Crie tests/unit/math.test.js:

    // tests/unit/math.test.js
    const multiply = require('../../src/services/math');

describe('Função de Multiplicação', () => { test('deve multiplicar dois números positivos corretamente', () => { expect(multiply(2, 3)).toBe(6); });

test('deve multiplicar um número positivo por zero corretamente', () => { expect(multiply(5, 0)).toBe(0); });

test('deve lançar um erro se o primeiro argumento não for um número', () => { expect(() => multiply('a', 5)).toThrow('Os argumentos devem ser números.'); });

test('deve lançar um erro se o segundo argumento não for um número', () => { expect(() => multiply(5, 'b')).toThrow('Os argumentos devem ser números.'); }); });

Como Testar e Validar o Resultado

Execute o comando no seu terminal:

npm test

Você verá a saída do Jest. Observe se:

    • Todos os 4 testes da Função de Multiplicação passaram.
    • O relatório de cobertura mostra os arquivos de src/services sendo cobertos e atinge os limites de 90% para linhas e 85% para funções. Se não atingir, você verá uma falha indicando isso.

Troubleshooting dos Erros Mais Comuns

    • jest: command not found ou Error: Cannot find module 'jest': Geralmente significa que o Jest não foi instalado corretamente ou não está no PATH. Verifique npm install --save-dev jest.
    • No tests found, exiting with code 1: O Jest não encontrou nenhum arquivo de teste que corresponda aos padrões em testMatch ou nos diretórios roots. Revise jest.config.js para garantir que os roots e testMatch apontem para seus arquivos de teste.
    • Coverage threshold not met: Você não atingiu os limites mínimos de cobertura que definiu em coverageThreshold. Isso indica que você precisa escrever mais testes para cobrir seu código ou ajustar os limites.
    • Erros de sintaxe JavaScript em testes: Verifique se seus arquivos de teste estão sintaticamente corretos. O Jest pode indicar a linha e coluna do erro.

Próximos Passos Sugeridos

Com essa configuração robusta, você está pronto para ir além!

    • Explore mais matchers do Jest (como toHaveBeenCalled, toMatchObject, resolves, rejects).
    • Aprenda a fazer mocking e stubbing com Jest para isolar partes do seu código durante os testes, o que é primordial para testes de unidade e integração de APIs.
    • Investigue a integração com bibliotecas como Supertest para realizar testes de integração em suas rotas Express, simulando requisições HTTP reais.
    • Considere a implementação de ts-jest se seu projeto migrar para TypeScript, para facilitar o teste de código TypeScript.

Parabéns, você agora domina a arte de configurar o Jest com padrões enterprise! Este é um passo essencial para desenvolver APIs mais resilientes e de alta qualidade.

🚀 Pronto para a próxima aula?

Continue sua jornada no desenvolvimento de APIs e domine Node.js & Express!

📚 Ver todas as aulas