Isolar com dublês de teste
Para testar uma unidade sem suas dependências reais (rede, BD, relógio, pagamentos) usamos dublês de teste (test doubles):
- Stub: devolve respostas predefinidas. Substitui uma dependência para que
devolva sempre o que o teste precisa (
obterUsuario() => { id: 1 }). - Spy (espião): envolve uma função real ou falsa e registra como foi chamada (quantas vezes, com quais argumentos). Serve para verificar interações.
- Mock: um dublê com expectativas programadas: já sabe quais chamadas deve receber e falha se não forem cumpridas. Combina stub (responde) e spy (verifica).
Na prática a linguagem é solta: muita gente chama de "mock" qualquer dublê. O importante é a intenção: stub = controlar entradas, spy/mock = verificar saídas/interações.
Injeção de dependências
Para poder substituir uma dependência por um dublê, a função não deve criá-la por dentro, deve recebê-la. Isso é injeção de dependências:
// Difícil de testar: a dependência está "soldada".
function registrar(email) { enviarEmailReal(email); }
// Fácil de testar: injetamos o colaborador.
function registrar(email, enviarEmail) { enviarEmail(email); }
// No teste passamos um espião e verificamos que foi chamado.
Testar funções puras e endpoints
- Funções puras (mesma entrada → mesma saída, sem efeitos): as mais fáceis. Você só afirma sobre o valor devolvido.
- Endpoints HTTP: um teste de integração levanta o handler e lhe envia uma requisição. No Node usa-se supertest sobre o app do Express:
// Conceitual (supertest não roda neste sandbox):
await request(app).get("/usuarios/1").expect(200);
Nestes exercícios simulamos essa requisição/resposta com crearReq() e
crearRes(), e afirmamos sobre res.statusCode e res.body.
Asserções (expect)
Uma asserção declara o que deve se cumprir; se não, lança e o teste falha.
A API expect(actual).toBe(esperado) é só açúcar sobre um if + throw.
Cobertura e seus limites
A cobertura (coverage) mede que porcentagem de linhas/ramos a suíte executou. Útil para encontrar zonas sem teste, mas não mede qualidade: você pode ter 100% de cobertura e zero asserções úteis. Cobertura alta ≠ código correto.
Exemplos
Um espião feito à mão
function criarEspiao() {
const espiao = (...args) => { espiao.chamadas.push(args); };
espiao.chamadas = [];
return espiao;
}
const enviar = criarEspiao();
function registrar(email, enviarEmail) { enviarEmail(email); }
registrar("ana@x.com", enviar);
console.log(enviar.chamadas.length === 1); // true
console.log(enviar.chamadas[0][0] === "ana@x.com"); // true