Testes E2E com Playwright: como testar formulários com CPF de forma confiável
Guia prático para escrever testes end-to-end com Playwright que validam formulários brasileiros com CPF, cobrindo cenários de sucesso, falha e edge cases.
28 de março de 2025 · 5 min de leitura
Por que os testes E2E de formulários com CPF costumam falhar
Testes end-to-end que envolvem CPF têm um problema clássico: os desenvolvedores hardcodam um CPF "de teste" no código (ex.: 123.456.789-09), e com o tempo esse CPF é banido, começa a disparar validações de negócio inesperadas, ou simplesmente gera dados inconsistentes entre execuções.
A solução é gerar CPFs fictícios dinamicamente em cada execução de teste, garantindo isolamento e confiabilidade.
Configurando o Playwright
npm init playwright@latest
Estrutura de projeto recomendada:
e2e/
├── fixtures/
│ ├── cpf.ts # gerador de CPF para testes
│ └── test-data.ts # factory de dados de teste
├── pages/
│ └── checkout.page.ts # Page Objects
├── tests/
│ └── checkout.spec.ts
└── playwright.config.ts
Criando o gerador de CPF para testes
// e2e/fixtures/cpf.ts
function randomDigit(): number {
return Math.floor(Math.random() * 10);
}
function calcVerifier(digits: number[], weights: number[]): number {
const sum = digits.reduce((acc, d, i) => acc + d * weights[i], 0);
const rem = sum % 11;
return rem < 2 ? 0 : 11 - rem;
}
export function generateTestCpf(uf?: string): string {
const UF_DIGITS: Record<string, number> = {
SP: 8, RJ: 7, MG: 6, RS: 0, PR: 9,
};
const base = Array.from({ length: 8 }, randomDigit);
base.push(uf ? (UF_DIGITS[uf] ?? randomDigit()) : randomDigit());
const v1 = calcVerifier(base, [10, 9, 8, 7, 6, 5, 4, 3, 2]);
base.push(v1);
const v2 = calcVerifier(base, [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]);
base.push(v2);
const raw = base.join("");
return raw.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
}
export function generateInvalidCpf(): string {
// CPF com dígito verificador errado (propositalmente inválido)
return "123.456.789-00";
}
Factory de dados de teste
// e2e/fixtures/test-data.ts
import { generateTestCpf } from "./cpf";
export interface TestUser {
name: string;
email: string;
cpf: string;
birthDate: string;
phone: string;
}
let counter = 0;
export function createTestUser(overrides: Partial<TestUser> = {}): TestUser {
counter++;
return {
name: `Usuário Teste ${counter}`,
email: `test.user.${counter}.${Date.now()}@example.com`,
cpf: generateTestCpf("SP"),
birthDate: "1990-05-15",
phone: "(11) 91234-5678",
...overrides,
};
}
Page Object para formulário de cadastro
// e2e/pages/register.page.ts
import { Page, Locator, expect } from "@playwright/test";
export class RegisterPage {
readonly nameInput: Locator;
readonly emailInput: Locator;
readonly cpfInput: Locator;
readonly birthDateInput: Locator;
readonly submitButton: Locator;
readonly cpfError: Locator;
readonly successMessage: Locator;
constructor(private page: Page) {
this.nameInput = page.getByLabel("Nome completo");
this.emailInput = page.getByLabel("E-mail");
this.cpfInput = page.getByLabel("CPF");
this.birthDateInput = page.getByLabel("Data de nascimento");
this.submitButton = page.getByRole("button", { name: "Criar conta" });
this.cpfError = page.getByTestId("cpf-error");
this.successMessage = page.getByTestId("success-message");
}
async goto() {
await this.page.goto("/cadastro");
}
async fillCpf(cpf: string) {
await this.cpfInput.fill(cpf);
await this.cpfInput.blur(); // Dispara validação onBlur
}
async fillForm(user: { name: string; email: string; cpf: string; birthDate: string }) {
await this.nameInput.fill(user.name);
await this.emailInput.fill(user.email);
await this.fillCpf(user.cpf);
await this.birthDateInput.fill(user.birthDate);
}
async submit() {
await this.submitButton.click();
}
}
Testes E2E completos
// e2e/tests/register.spec.ts
import { test, expect } from "@playwright/test";
import { RegisterPage } from "../pages/register.page";
import { createTestUser, generateInvalidCpf } from "../fixtures/test-data";
test.describe("Formulário de cadastro — validação de CPF", () => {
let registerPage: RegisterPage;
test.beforeEach(async ({ page }) => {
registerPage = new RegisterPage(page);
await registerPage.goto();
});
test("aceita CPF válido e submete o formulário com sucesso", async () => {
const user = createTestUser();
await registerPage.fillForm(user);
await registerPage.submit();
await expect(registerPage.successMessage).toBeVisible();
await expect(registerPage.successMessage).toContainText("Cadastro realizado");
});
test("rejeita CPF com dígito verificador incorreto", async () => {
await registerPage.fillCpf(generateInvalidCpf());
await expect(registerPage.cpfError).toBeVisible();
await expect(registerPage.cpfError).toHaveText("CPF inválido");
});
test("rejeita sequência de dígitos repetidos", async () => {
await registerPage.fillCpf("111.111.111-11");
await expect(registerPage.cpfError).toBeVisible();
});
test("rejeita CPF com menos de 11 dígitos", async () => {
await registerPage.fillCpf("123.456");
await registerPage.submit();
await expect(registerPage.cpfError).toBeVisible();
});
test("aceita CPF sem máscara e formata automaticamente", async () => {
await registerPage.fillCpf("12345678909");
await expect(registerPage.cpfInput).toHaveValue("123.456.789-09");
});
test("gera CPFs únicos entre execuções de teste", async ({ page }) => {
const users = Array.from({ length: 5 }, createTestUser);
const cpfs = users.map((u) => u.cpf);
const uniqueCpfs = new Set(cpfs);
expect(uniqueCpfs.size).toBe(5);
});
});
test.describe("Formulário de cadastro — estados brasileiros", () => {
test("aceita CPF de São Paulo (dígito regional 8)", async ({ page }) => {
const registerPage = new RegisterPage(page);
await registerPage.goto();
const user = createTestUser({ cpf: generateTestCpf("SP") });
await registerPage.fillForm(user);
await registerPage.submit();
await expect(registerPage.successMessage).toBeVisible();
});
});
Testes de performance com múltiplos CPFs
Para testes de carga de formulários (ex.: validar que o servidor aguenta 100 submissões por segundo):
// e2e/tests/load.spec.ts
import { test } from "@playwright/test";
import { createTestUser } from "../fixtures/test-data";
test("submete 50 cadastros em paralelo", async ({ browser }) => {
const users = Array.from({ length: 50 }, createTestUser);
await Promise.all(
users.map(async (user) => {
const page = await browser.newPage();
await page.goto("/cadastro");
await page.getByLabel("Nome completo").fill(user.name);
await page.getByLabel("E-mail").fill(user.email);
await page.getByLabel("CPF").fill(user.cpf);
await page.getByRole("button", { name: "Criar conta" }).click();
await page.waitForSelector("[data-testid='success-message']");
await page.close();
})
);
});
Configuração de CI com GitHub Actions
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npx playwright install --with-deps chromium
- name: Run E2E tests
run: npx playwright test
env:
BASE_URL: ${{ secrets.STAGING_URL }}
- uses: actions/upload-artifact@v4
if: failure()
with:
name: playwright-report
path: playwright-report/
Conclusão
Testes E2E robustos para formulários brasileiros exigem geração dinâmica de CPFs, Page Objects bem estruturados e cobertura tanto de caminhos felizes quanto de casos de erro. Com Playwright e um gerador de CPF sintético integrado aos fixtures, você garante que os testes são independentes, determinísticos e em total conformidade com a LGPD.
Use o Gerador de CPF para explorar a ferramenta manualmente e o código desta página para automatizar a geração nos seus testes.
Precisa gerar CPFs para testar sua implementação?
Usar o Gerador de CPF →