PlaywrightTestes E2EQACPFAutomação

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 →