Jest: testes unitários que valem a pena
Teste unitário ruim é pior que ausente. A regra de ouro dos mocks (fronteiras, nunca a lógica), os hábitos que mantêm a suíte viva e as armadilhas de snapshot e cobertura.
Já defendemos a base larga da pirâmide: milhares de testes unitários rápidos. No ecossistema JavaScript, a ferramenta padrão dessa base é o Jest (jestjs.io): test runner, asserções, mocks e cobertura numa caixa só, com configuração quase zero. Este artigo é sobre usá-lo bem, porque teste unitário ruim é pior que ausente: dá trabalho e não dá confiança.
O básico que já resolve muito
// desconto.test.js
import { calcularDesconto } from "./desconto";
describe("calcularDesconto", () => {
it("aplica 10% para a primeira compra", () => {
expect(calcularDesconto({ primeiraCompra: true, total: 200 })).toBe(20);
});
it("não aplica desconto acima do teto", () => {
expect(calcularDesconto({ primeiraCompra: true, total: 10_000 })).toBe(500);
});
it.each([
[0, 0],
[-50, 0],
])("trata total inválido %p", (total, esperado) => {
expect(calcularDesconto({ primeiraCompra: true, total })).toBe(esperado);
});
});
Repare nos hábitos embutidos: nomes que descrevem comportamento (o relatório de testes vira documentação), casos de borda com it.each, e uma função pura sendo testada em milissegundos, sem mock nenhum. Esse é o teste unitário ideal, e ele é consequência de código com fronteiras: lógica isolada é lógica testável.
Mocks: a ferramenta que define a qualidade da suíte
O Jest mocka qualquer coisa (jest.fn(), jest.mock()), e é exatamente por isso que exige critério. A regra que separa suítes boas de pesadelos:
Mocke as fronteiras (HTTP, banco, relógio, aleatoriedade). Nunca mocke a própria lógica.
// ✅ Fronteira mockada: o teste controla o mundo externo
jest.mock("./gateway-pagamento");
gateway.cobrar.mockResolvedValue({ status: "aprovado" });
const resultado = await confirmarPedido(pedido);
expect(resultado.status).toBe("confirmado");
expect(gateway.cobrar).toHaveBeenCalledWith(pedido.clienteId, 180);
O antipadrão correspondente: mockar módulos internos do próprio domínio. O teste passa a afirmar como o código faz (chamou a função X com Y) em vez de o que ele produz, e cada refatoração quebra dezenas de testes sem nenhum bug real. Teste frágil ensina o time a ignorar vermelho, o pior destino possível.
Dois mocks que sempre valem: o relógio (jest.useFakeTimers() para testar debounce, expiração, fusos) e a aleatoriedade: determinismo é pré-requisito de suíte confiável.

Os hábitos que mantêm a suíte viva
- Arrange-Act-Assert visível: prepara, executa, afirma: um comportamento por teste. Teste com 8 asserções sobre coisas diferentes é relatório ruim quando falha.
- Dados de teste com builders:
criarPedido({ total: 200 })com defaults sensatos, em vez de 15 linhas de objeto literal repetidas em cada teste. --watchno fluxo: o Jest rodando os testes afetados a cada salvamento é o ciclo de feedback de segundos que muda o jeito de programar.- Cobertura como bússola, nunca como meta: o relatório aponta o que está despido; os 100% continuam não sendo objetivo. Um limiar razoável no CI (para não regredir) basta.
- Snapshot com moderação: ótimo para saídas serializadas pequenas e estáveis; péssimo como "teste de tudo": snapshots gigantes viram carimbo de
--updateSnapshotsem leitura.
E o Vitest?
A pergunta atual do ecossistema. O Vitest oferece API praticamente idêntica com performance melhor em projetos Vite/ESM. A boa notícia: tudo neste artigo vale igual: os princípios de mock, os hábitos e as armadilhas são os mesmos; a migração é quase mecânica. Escolha pelo seu bundler, não por moda.
Estruturar essa base (config, builders, política de mocks e o limiar no CI) é o primeiro degrau da esteira de qualidade que montamos no serviço de Qualidade e Testes.
Seus testes quebram quando você refatora sem mudar comportamento? É o sintoma clássico de mock demais. Cole um teste na conversa com a IA da Rabelo Digital, aqui no canto da tela, e ela mostra a versão robusta. Sem formulário, sem espera.


Deixe um Comentário