Testes E2E com Playwright: Guia Prático Completo

Testes E2E com Playwright: Guia Prático Completo

“`html





Testes E2E com Playwright: Guia Prático | yurideveloper.com.br

Guia prático • Playwright • Testes E2E

Testes E2E com Playwright: guia prático

Vou te mostrar como estruturar testes de ponta a ponta com Playwright de um jeito estável, legível e pronto para rodar localmente e em CI,
evitando os problemas clássicos de flakiness.

1) Estrutura do projeto e estratégia de execução

Antes de escrever qualquer teste, eu organizo o projeto para reduzir ruído e facilitar manutenção. Em E2E, a maior fonte de custo é o “ciclo
de erro”: quanto mais tempo você perde para identificar o que quebrou e por quê, mais a suíte vira um problema.

Minhas escolhas para começar bem:

  • Um padrão de pastas para pages/fluxos, fixtures e arquivos de testes.
  • Separar testes por intenção (smoke, regressão, pagamentos, permissões etc.).
  • Rodar rápido: ter um subconjunto curto que roda em todo commit e um completo em agendamentos.

Separação por tags ajuda muito:

  • Smoke: valida rotas e health do fluxo (rápido).
  • Regressão: cobre cenários mais detalhados (mais pesado).
  • Testes sensíveis: mexem com dados de produção simulada, permissões complexas, integrações.

Regra de ouro: evite testes gigantes que fazem “tudo ao mesmo tempo”. Se um cenário falha, você quer identificar o
ponto exato sem reexecutar o resto.

2) Locators confiáveis e validações úteis

A estabilidade dos E2E costuma depender mais de como você encontra elementos do que de “esperas” no código.
Eu prefiro locators que representem o que o usuário vê e faz, e não detalhes frágeis de implementação.

Boas práticas de locator:

  • Preferir “papéis” e acessibilidade: botões por nome, campos por label, itens por role.
  • Usar data-testid com critério quando não existir alternativa semântica.
  • Evitar seletores genéricos (ex.: div:nth-child, classes que mudam com frequência).
  • Construir a asserção com contexto (ex.: dentro do formulário específico).

Validações melhores são específicas e significativas: em vez de só “URL mudou”, eu também verifico conteúdo esperado,
estado do botão e mensagens de erro.

Um padrão que funciona bem é: localizar → interagir → aguardar estado verificável → validar conteúdo.
Isso elimina a dependência de tempos e foca no “resultado” do comportamento.

3) Autenticação, estado e isolamento entre testes

Flakiness em E2E frequentemente nasce do estado global: cookies, localStorage, permissões e dados de sessão.
Para mim, autenticação é onde eu mais invisto tempo em previsibilidade.

Três estratégias (da mais prática à mais robusta):

  • Login por teste: funciona no começo, mas aumenta tempo e chance de falha por UI.
  • Login uma vez e reusar estado: melhor equilíbrio; reduz repetição.
  • Isolar por contexto: cada teste roda com seu estado, garantindo independência.

O que eu considero “estado correto”:

  • Usuário autenticado no ambiente correto.
  • Permissões equivalentes ao que o teste pretende validar.
  • Dados coerentes (sem depender de listas “que podem estar vazias”).

Se você precisa testar várias permissões, crie um “perfil” por conjunto de dados e serialize o estado de cada um.
Isso deixa seus testes mais claros e menos verbosos.

4) Estabilidade: waits corretos, logs e execução em CI

Playwright já ajuda bastante com espera automática para ações e condições. O segredo é você não “combinar” waits fixas com
lógica que deveria ser condicional.

Checklist de estabilidade:

  • Evitar wait for timeout (esperas fixas): prefira validar condições (visível, habilitado, texto presente).
  • Usar expectativa antes da validação: “depois de clicar, o que deve aparecer?”
  • Garantir consistência em endpoints e dados: quando possível, prepare dados de teste.
  • Habilitar evidências (screenshots/vídeos/trace) para reduzir tempo de investigação.

Em CI, eu gosto de tratar a suíte como parte do produto: coletar diagnóstico quando falha e manter logs fáceis de correlacionar com o teste.

Quando falhar, você deve conseguir responder rapidamente: qual teste, em que etapa, qual estado e qual evidência.
O objetivo é encurtar o “tempo até o conserto”.

Exemplo prático: autenticar uma vez e testar um fluxo E2E com validações

Abaixo vai um exemplo com storageState para reaproveitar autenticação e um teste que valida o resultado do fluxo,
usando expectativas focadas no comportamento.

playwright.config + teste E2E (TypeScript)
e2e/ auth

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests/e2e',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 2 : undefined,
  reporter: [['html', { outputFolder: 'playwright-report' }]],
  use: {
    baseURL: process.env.BASE_URL || 'http://localhost:3000',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
  ],
});
// tests/e2e/auth-and-dashboard.spec.ts
import { test, expect } from '@playwright/test';

test.describe('Autenticado - Dashboard', () => {
  test.use({ storageState: 'tests/fixtures/auth-state.json' });

  test('deve abrir o dashboard e exibir os cards esperados', async ({ page }) => {
    await page.goto('/dashboard');

    // Locators semânticos e validações do que importa
    await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

    const cards = page.locator('[data-testid="dashboard-card"]');
    await expect(cards.first()).toBeVisible();
    await expect(cards).toHaveCountGreaterThan(0); // se quiser, remova caso não exista util; veja ajuste abaixo.

    // Exemplo: valida um item específico
    await expect(page.getByText('Resumo do mês', { exact: false })).toBeVisible();
  });

  test('deve permitir navegar para o perfil do usuário', async ({ page }) => {
    await page.goto('/dashboard');

    await page.getByRole('button', { name: 'Meu perfil' }).click();

    await expect(page).toHaveURL(/\/perfil$/);
    await expect(page.getByRole('heading', { name: 'Seu perfil' })).toBeVisible();
  });
});
// scripts/generate-auth-state.ts (script opcional para criar tests/fixtures/auth-state.json)
import { chromium } from '@playwright/test';

async function main() {
  const browser = await chromium.launch();
  const context = await browser.newContext();

  const page = await context.newPage();
  await page.goto(process.env.BASE_URL || 'http://localhost:3000/login');

  await page.getByLabel('Email').fill(process.env.TEST_EMAIL || 'user@example.com');
  await page.getByLabel('Senha').fill(process.env.TEST_PASSWORD || 'password');

  await page.getByRole('button', { name: 'Entrar' }).click();

  // Garanta que está logado antes de salvar
  await page.waitForURL(/\/dashboard/);

  await context.storageState({ path: 'tests/fixtures/auth-state.json' });
  await browser.close();
}

main().catch(err => {
  console.error(err);
  process.exit(1);
});

Observação: se você não quiser usar utilitário como toHaveCountGreaterThan, substitua por
const count = await cards.count(); expect(count).toBeGreaterThan(0).

Quer deixar sua suíte ainda mais forte?

Agora que você tem um caminho prático para testes E2E com Playwright, vale a pena continuar evoluindo:
além de organizar por intenção, foque em locators confiáveis, estado previsível e evidências em falhas.

Se você quiser, comente quais cenários do seu sistema você quer cobrir (login, permissões, checkout, etc.).

yurideveloper.com.br • Guia prático de testes E2E com Playwright



“`