O que é um teste E2E
Um teste end-to-end automatiza um navegador real (ou headless) para reproduzir um fluxo completo de usuário: abre a página, escreve em um formulário, clica, espera a resposta do backend e verifica o que vê. Se passa, você tem uma prova forte de que o sistema inteiro funciona em conjunto.
Ferramentas: Playwright e Cypress
- Playwright (Microsoft): controla Chromium, Firefox e WebKit com a mesma API; bom auto-waiting, paralelismo e traces.
- Cypress: roda dentro do próprio navegador, com uma experiência de depuração muito visual.
// Conceitual (Playwright não roda neste sandbox):
await page.goto("/login");
await page.fill("#email", "ana@x.com");
await page.click("text=Entrar");
await expect(page.getByText("Bem-vinda")).toBeVisible();
Quando vale a pena?
Reserve os E2E para os fluxos críticos de negócio: cadastro, login, compra, o "caminho feliz" que não pode quebrar. Não tente cobrir cada caso limite com E2E; isso se faz melhor com unitários/integração (mais baratos e precisos).
Flakiness (fragilidade)
Um teste flaky passa às vezes e falha outras sem que o código mude. É o maior inimigo dos E2E: corrói a confiança na suíte. Causas e remédios:
- Esperas fixas (
sleep(500)): substitua-as por esperas por uma condição (que o elemento esteja visível, que a rede termine). As boas ferramentas fazem auto-waiting. - Seletores frágeis (classes CSS de estilo): use seletores estáveis como
data-testid. - Dados compartilhados / ordem: cada teste deve preparar e limpar seus próprios dados e ser independente do resto.
Testes na CI
Na integração contínua (CI) a suíte é executada automaticamente a cada push ou pull request, normalmente com o navegador em modo headless. Boas práticas: executar os unitários primeiro (falham rápido e barato), paralelizar, guardar capturas/vídeos/traces dos E2E que falharam para depurar, e bloquear o merge se a suíte não passar.
Exemplos
Esperar por uma condição, não por um tempo fixo
// Ruim: frágil a variações de tempo.
// await sleep(500); expect(visible(botao));
// Bom: esperamos que a condição se cumpra.
async function esperarAte(cond, tentativas = 10) {
for (let i = 0; i < tentativas; i++) {
if (cond()) return true;
await new Promise((r) => setTimeout(r, 10));
}
return false;
}
let pronto = false;
setTimeout(() => { pronto = true; }, 30);
console.log(await esperarAte(() => pronto)); // true