Executando verificação de segurança...
5

Testes Unitários em JavaScript com Jest: Guia Básico

Introdução: A Essência dos Testes Unitários e Por Que Jest?

Jornada Jest concluída: testes unitários, do básico ao avançado (mocks, snapshots). Você domina a testabilidade. Testes são o alicerce de software robusto; permitem refatorar e inovar com segurança, garantindo qualidade e longevidade. Maestria exige prática contínua: teste é sua superpotência. Explore integração e E2E, aprimore-se e construa um futuro sólido.

Configurando Seu Ambiente: Jest em Minutos

Testes unitários são cruciais à qualidade do software, validando pequenas unidades de código isoladamente. Benefícios: confiança, refatoração segura, detecção precoce e barata de bugs. Ausência causa regressões e débito técnico.

Para JavaScript, Jest (Facebook/React) é o framework padrão. Ele oferece "zero-config", velocidade (paralela) e um ecossistema completo (runner, assertions, mocking, cobertura).

Este guia cobrirá Jest do básico (setup, sintaxe) ao avançado (async, mocking, snapshots), para código JS robusto. Começaremos configurando Jest via "zero-config" para testar rápido.

Os Pilares do Teste: describe, test/it e expect com Matchers

Este guia rápido o levará do zero ao seu primeiro teste Jest.

Pré-requisitos

Node.js e npm (ou Yarn) são necessários. Verifique:

node -v
npm -v # ou yarn -v

Instalação e Configuração

  1. Inicie um projeto Node.js:
    mkdir meu-projeto-jest && cd meu-projeto-jest
    npm init -y # ou yarn init -y
    
  2. Instale Jest:
    npm install --save-dev jest # ou yarn add --dev jest
    
  3. Adicione test script ao package.json:
    {
      "scripts": {
        "test": "jest"
      }
    }
    

Estrutura de Testes

Jest busca .test.js (ou .spec.js) ou pastas __tests__.

Seu Primeiro Teste

  1. src/sum.js:
    function sum(a, b) { return a + b; }
    module.exports = sum;
    
  2. src/sum.test.js:
    const sum = require('./sum');
    test('soma 1 + 2 para ser 3', () => {
      expect(sum(1, 2)).toBe(3);
    });
    
  3. Execute:
    npm test # ou yarn test
    

Lidando com o Tempo: Testes Assíncronos com Jest

A função expect() e seus matchers são o cerne dos testes Jest, verificando o comportamento do código. expect(valor) recebe o que testar, e o matcher realiza a comparação.

Matchers Essenciais

  • toBe(): Igualdade estrita (===) para primitivos.
  • toEqual(): Igualdade de conteúdo para objetos/arrays (recursivo).
  • .not: Inverte qualquer asserção (ex: not.toBe()).
  • toBeNull(), toBeUndefined(), toBeTruthy(), toBeFalsy(): Verificam estados específicos.
  • toContain(): Checa presença em array ou substring.
  • toHaveLength(): Verifica tamanho de array ou string.
  • toMatch(): Compara strings via expressão regular.
test('exemplos de matchers', () => {
  expect(2 + 2).toBe(4);
  expect({a:1}).toEqual({a:1});
  expect(5).not.toBe(3);
  expect(null).toBeNull();
  expect([1,2]).toContain(2);
  expect('abc').toHaveLength(3);
  expect('[email protected]').toMatch(/@/);
});

Próximo: testes assíncronos.

O Poder da Simulação: Mocks e Spies para Isolar o Código

Testar código assíncrono (APIs, timers) em JavaScript moderno é um desafio. Por padrão, Jest executa testes síncronos, não aguardando operações assíncronas (ex: setTimeout, Promises), o que causa falhas ou falsos positivos.

Como o Jest Lida com Assincronismo

  1. Callbacks (done()): Se um teste recebe done como argumento, Jest aguarda sua chamada para finalizar. Essencial para callbacks assíncronos, se done() não for chamado, o teste falha por timeout.

  2. Promises (.then(), expect.resolves, expect.rejects): Ao retornar uma Promise no teste, Jest aguarda sua resolução ou rejeição. As asserções expect.resolves e expect.rejects são as formas preferenciais para testar Promises diretamente, sendo mais claras que .then()/.catch(). Sempre retorne a Promise.

  3. async/await (Preferencial): A forma moderna, mais legível e robusta. Marque a função de teste como async. Use await para esperar a resolução de Promises. Para rejeições, await expect(promessa).rejects.toBe('erro') é a melhor prática, ou try/catch (mais verboso).

Melhor Prática: A combinação de async/await com expect.resolves e expect.rejects é a maneira mais recomendada, oferecendo clareza e concisão.

Dominar estas técnicas é vital para testar código assíncrono. Para isolar dependências externas, o próximo passo são os mocks e spies.

Além do Básico: Matchers Avançados e Personalizados

Para testes isolados, simular dependências externas é crucial. Mocks e spies do Jest são vitais para testes rápidos e focados na lógica.

  • Stub: Objeto com respostas pré-programadas.
  • Mock: Um stub que registra interações para verificação de comportamento. jest.fn() cria mocks customizáveis para controlar retornos (mockReturnValue), lógica (mockImplementation) e Promises (mockResolvedValue).
  • Spy: Monitora uma função existente sem alterar sua lógica original. jest.spyOn() cria spies; mockRestore() reverte alterações.

jest.mock() simula módulos inteiros, definindo seus exports mockados para isolamento.

Para limpeza e gerenciamento de estado entre testes:

  • mockClear(): Limpa o histórico de chamadas.
  • mockReset(): Limpa chamadas e a implementação do mock.
  • mockRestore(): (Para spies) Restaura a função original espionada.
    É comum usar beforeEach/afterEach para automatizar essa limpeza.

Dominar mocks e spies do Jest é essencial para testes unitários eficazes e confiáveis.

Gerenciando o Ciclo de Vida: Hooks de Setup e Teardown

Para cenários complexos, Jest oferece matchers avançados e a flexibilidade para criar os seus próprios, elevando a expressividade e precisão das validações.

Matchers para Mocks e Spies

Verifique a interação de funções mockadas:

  • toHaveBeenCalled(): Chamada ao menos uma vez.
  • toHaveBeenCalledTimes(n): Chamada n vezes.
  • toHaveBeenCalledWith(...args): Chamada com argumentos específicos.
// Ex: expect(mockFuncao).toHaveBeenCalled();

Testando Erros e Exceções

Garanta o tratamento correto de erros:

  • toThrow() / toThrowError(): Verifica se uma função lança erro (qualquer, mensagem, tipo, regex). Envolva a chamada em uma função anônima: expect(() => func()).toThrow().
// Ex: expect(() => dividir(10,0)).toThrow('erro');

Testes de Snapshot (toMatchSnapshot())

Garanta consistência de UI/dados:

  • Salva a primeira saída serializada e compara em execuções futuras. Falha indica mudança; atualize (u) se intencional. Ideal para estruturas complexas.
// Ex: expect(componente).toMatchSnapshot();

Matchers Flexíveis

Para validações menos exatas:

  • expect.anything(): Não null ou undefined.
  • expect.any(Constructor): Instância de Constructor (ex: Date, String).
  • expect.arrayContaining(array): Array real contém elementos do array esperado.
  • expect.objectContaining(object): Objeto real contém propriedades e valores do object esperado.

Criando Custom Matchers

Aumente a legibilidade e manutenibilidade para lógica complexa/repetitiva:

  1. Defina a função do matcher com expect.extend({ seuMatcher(received, arg) { /* lógica */ return { pass, message } } }).
  2. Configure setupFilesAfterEnv em jest.config.js para carregar o arquivo dos matchers.
  3. Use em testes: expect(valor).seuMatcher(arg).
// Ex: expect(10).toBeDivisibleBy(2);

Próximo: Ganchos beforeEach/afterEach otimizam a organização e garantem estado limpo em testes.

Garantindo a Qualidade: Análise de Cobertura de Teste

Testes unitários exigem isolamento e evitam duplicação. Jest oferece hooks de ciclo de vida para testes limpos, independentes e confiáveis.

Hooks de Ciclo de Vida Jest

beforeAll()/afterAll() gerenciam setup/teardown uma única vez por grupo (describe ou arquivo). beforeEach()/afterEach() executam antes/depois de cada teste, garantindo um estado limpo e isolamento. Hooks aninhados seguem uma ordem de execução hierárquica. Dominar esses hooks é essencial para testes robustos e de fácil depuração.

Escrevendo Testes Excelentes: Dicas e Melhores Práticas

A cobertura de teste mede a % do código executado pelos testes. Tipos chave: Linha, Branch, Função.

Jest gera relatórios com --coverage. Saídas: resumo no terminal, HTML interativo (verde = coberto, vermelho = não coberto) em coverage/lcov-report, JSON/XML.

Interprete relatórios HTML: identifique lacunas (linhas vermelhas) para priorizar testes.

Jest impõe limiares mínimos de cobertura via coverageThreshold em package.json/jest.config.js. Falha se limites não forem atingidos. collectCoverageFrom define arquivos.

// package.json ou jest.config.js
{
  "jest": {
    "coverageThreshold": {
      "global": { "branches": 80, "functions": 80, "lines": 80, "statements": 80 }
    },
    "collectCoverageFrom": ["src/**/*.{js,jsx,ts,tsx}"]
  }
}

Limitações: Alta cobertura não garante qualidade ou testes eficazes. Indica o que rodou, não como. Foque em testes críticos e significativos.

Ferramentas: IDEs (Coverage Gutters), CI/CD (Codecov, SonarQube) para visualização, rastreamento e aplicação.

Cobertura e testes robustos são cruciais para a qualidade do software.

Conclusão: O Caminho para o Código Robusto e Confiável

Testes unitários eficazes exigem diretrizes para legibilidade e manutenção.

Princípios F.I.R.S.T. para Testes de Qualidade

  • Fast (Rápidos): Executam rapidamente para feedback rápido.
  • Isolated (Isolados): Independentes, ordem não importa.
  • Repeatable (Repetíveis): Resultados consistentes, sempre.
  • Self-Validating (Auto-validáveis): Resultado claro: passou ou falhou.
  • Timely (Oportunos): Escritos antes/com o código (TDD).

Estruturando Testes com Arrange-Act-Assert (AAA)

  1. Arrange: Prepara o ambiente.
  2. Act: Executa o código.
  3. Assert: Verifica o resultado.

Nomenclatura Descritiva: describe (unidade), test (comportamento específico).

Um Conceito por Teste: Focar em um único conceito.
Evitar Lógica Complexa: Testes simples; use auxiliares/hooks.
Testes Independentes: Garanta estado limpo (beforeEach/afterEach).
Refatorar Testes: Mantenha testes limpos e eficientes como código.

.only e .skip: Úteis em desenvolvimento, mas nunca commit.

Configuração Avançada do Jest (jest.config.js): Personalize o Jest.

  • moduleNameMapper: Mapeamento de módulos.
  • setupFilesAfterEnv: Config global.
  • collectCoverage: Relatórios cobertura.
  • testEnvironment: Ambiente.
  • testPathIgnorePatterns: Ignorar paths.

Essas práticas garantem testes de alta qualidade, documentação e resiliência do software.

Carregando publicação patrocinada...
2