O que é Playwright?
Playwright é um framework de testes end-to-end cross-browser da Microsoft. Ele controla Chromium, Firefox e WebKit através de uma única API, aguarda automaticamente que os elementos estejam prontos e fornece um test runner com fixtures, paralelismo, tracing e controle de rede.
Ele surgiu a partir do Puppeteer e manteve as melhores partes — um protocolo moderno e uma API limpa — enquanto adicionava suporte cross-browser real e ferramentas de primeira classe. Para equipes que estão iniciando uma suíte de testes end-to-end hoje, ele é a escolha padrão mais comum, e seu auto-waiting é o principal motivo para que seus testes sejam menos instáveis (flaky) do que em ferramentas mais antigas.
Escrevendo um teste
Um teste navega, interage e faz asserções. O Playwright fornece as funções test e expect e a fixture page.
// tests/login.spec.ts
import { test, expect } from "@playwright/test";
test("user can sign in", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
});
A fixture page é uma aba do navegador. Ações como click e fill aguardam que o elemento esteja acionável antes de agir, portanto, não é necessário inserir sleeps nem há risco de race conditions.
Locators e auto-waiting
Locators são queries “preguiçosas” (lazy) que resolvem para um elemento quando são utilizados, e eles tentam novamente até que o elemento esteja pronto. Prefira locators voltados para o usuário nesta ordem:
- getByRole — botões, links, cabeçalhos e inputs por papel acessível (accessible role) e nome.
- getByLabel — controles de formulário por label.
- getByText — texto visível.
- getByPlaceholder, getByAltText, getByTitle — outros atributos visíveis.
- getByTestId — um id de teste estável quando nada voltado para o usuário for adequado.
// locators.ts
page.getByRole("link", { name: "Pricing" });
page.getByLabel("Search");
page.getByText("Welcome back");
page.getByTestId("cart-total");
Como as ações possuem auto-wait, raramente você precisará de esperas explícitas. As asserções também são “web-first”: expect(locator).toBeVisible() tenta novamente até que a condição seja atendida ou o timeout expire, o que torna os testes resilientes a problemas de timing.
// assertions.ts
await expect(page.getByRole("alert")).toHaveText("Saved");
await expect(page.getByRole("button", { name: "Save" })).toBeEnabled();
await expect(page.getByTestId("row")).toHaveCount(3);
Fixtures e configuração compartilhada
O sistema de fixtures do Playwright é a maneira de compartilhar configurações sem precisar repeti-las. Uma fixture é um valor injetado nos testes, criado e finalizado pelo runner.
// fixtures.ts
import { test as base } from "@playwright/test";
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
await page.goto("/login");
await page.getByLabel("Email").fill("[email protected]");
await page.getByLabel("Password").fill("secret");
await page.getByRole("button", { name: "Sign in" }).click();
await use(page);
},
});
Especificamente para autenticação, o padrão recomendado é um projeto de setup que realiza o login uma única vez, salva o estado do armazenamento (storage state) em um arquivo e o reutiliza entre os testes. Isso mantém a suíte rápida e evita a repetição do fluxo de login.
Controle de rede
page.route intercepta requisições, permitindo que você teste estados de carregamento, erros e casos extremos sem a necessidade de um backend real.
// mock.ts
await page.route("**/api/users", (route) =>
route.fulfill({
status: 500,
body: JSON.stringify({ message: "Server error" }),
}),
);
await page.goto("/users");
await expect(page.getByRole("alert")).toContainText("went wrong");
Você também pode fazer asserções sobre o que o app enviou, atrasar respostas para testar estados de loading ou bloquear scripts de terceiros para manter os testes rápidos e determinísticos.
Debugging e ferramentas
As ferramentas do Playwright são grande parte do seu apelo:
- Codegen grava suas interações e gera um arquivo de teste com locators.
- UI mode executa testes em uma interface de watch onde você pode avançar passo a passo pelas ações e inspecionar o DOM.
- Trace viewer grava um trace completo — screenshots, snapshots do DOM, rede e console — que você pode reproduzir após uma falha no CI.
- Reporters produzem relatórios em HTML, JUnit e outros formatos nativamente.
O trace viewer, em particular, transforma uma falha misteriosa no CI em uma reprodução passo a passo, o que geralmente é a diferença entre um ajuste de cinco minutos e uma tarde inteira tentando adivinhar o erro.
Paralelismo e CI
Por padrão, o Playwright Test executa os arquivos de teste em paralelo através de processos worker. Você pode executar a mesma suíte em diferentes projetos de browser, ajustar a quantidade de workers e distribuir (shard) uma suíte grande entre várias máquinas com --shard. Para paralelizar com segurança, os testes devem ser independentes e evitar estados mutáveis compartilhados.
No CI, instale os browsers com npx playwright install --with-deps, execute a suíte e faça o upload do relatório HTML e dos traces como artefatos. Retentativas (retries) estão disponíveis para infraestruturas genuinamente instáveis, mas devem servir como uma rede de segurança, e não como um substituto para a correção de testes flaky.
Onde o Playwright se encaixa
Testes end-to-end estão no topo da pirâmide: são poucos, lentos e de alto valor. Abaixo deles, ficam os testes de componente com Testing Library e testes unitários rápidos com Vitest ou Jest. O Playwright cobre as jornadas que apenas um navegador real pode verificar — autenticação, checkout, navegação e comportamento cross-browser.
Melhores práticas
- Prefira locators de role e label; use test ids com moderação.
- Confie no auto-waiting em vez de
waitForTimeout. - Mantenha os testes independentes para que possam ser executados em paralelo com segurança.
- Reutilize a autenticação através do storage state em vez de fazer login a cada vez.
- Faça mock da rede para casos extremos; mantenha alguns testes contra o backend real.
- Grave traces em caso de falha e faça o upload deles no CI.
- Use
data-testidapenas para elementos que não possuam identidade acessível.
Erros comuns
- Usar seletores CSS ou XPath vinculados à estilização e estrutura.
- Utilizar esperas fixas (hard waits) com
waitForTimeout, que tornam a suíte lenta e mascaram race conditions. - Compartilhar estado entre testes, quebrando o paralelismo.
- Testar todos os casos de borda (edge cases) de ponta a ponta em vez de movê-los para a base da pirâmide.
- Ignorar traces e tentar adivinhar a causa de falhas no CI.
- Depender de retries para mascarar instabilidades (flakiness) reais.
Próximos passos
O Playwright é a escolha padrão mais robusta para testes end-to-end atualmente. Compare-o com o Cypress para conhecer uma experiência de desenvolvimento alternativa, e construa as camadas inferiores com Vitest e Testing Library. Em seguida, adicione uma única jornada crítica do usuário como seu primeiro teste e2e e evolua a partir daí.