Advanced (Atualizado: 02/06/2026)

Estratégia de testes com Claude Code: Vitest, Testing Library, Playwright e CI

Planeje testes unitários, integração, E2E e CI com Claude Code sem criar testes frágeis.

Estratégia de testes com Claude Code: Vitest, Testing Library, Playwright e CI

Uma estratégia de testes não é apenas escrever mais testes. É decidir o que deve ser protegido por testes unitários rápidos, que fronteiras precisam de testes de integração e quais fluxos de negócio merecem E2E.

Se você pedir ao Claude Code apenas “adicione testes”, ele pode gerar assertions superficiais, seletores CSS frágeis ou testes que só espelham a implementação atual. Um pedido melhor informa a camada de teste, o comportamento esperado, o comando a executar e os limites de edição.

Este guia foi conferido com a documentação oficial de Claude Code common workflows, Vitest coverage, Testing Library queries e Playwright para locators, assertions e CI.

Comece pela pirâmide

flowchart TB
  E2E["E2E: cadastro, checkout, CTA de receita"]
  INT["Integração: API, DB, formulários, componentes"]
  UNIT["Unitário: cálculo, validação, permissões"]
  E2E --> INT --> UNIT
CamadaReferênciaO que protegeFerramentas
Unitária60-70%lógica pura, validação, permissõesVitest
Integração20-30%componentes, API, fronteiras de DBVitest + Testing Library
E2E5-10%cadastro, compra, caminhos de receitaPlaywright
CI gatetodo PRlint, tipos, testes, relatóriosGitHub Actions

Lógica de preço fica em teste unitário. Um botão de checkout fica em teste de integração. A navegação de um CTA do artigo para a página de produtos é um bom E2E.

Configuração mínima

npm i -D vitest @vitest/coverage-v8 jsdom \
  @testing-library/react @testing-library/jest-dom @testing-library/user-event \
  @playwright/test
npx playwright install --with-deps
{
  "scripts": {
    "test": "vitest --run",
    "test:watch": "vitest",
    "test:coverage": "vitest --run --coverage",
    "test:e2e": "playwright test"
  }
}
// vitest.config.ts
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    environment: "jsdom",
    setupFiles: ["./test/setup.ts"],
    coverage: {
      provider: "v8",
      reporter: ["text", "html"],
      thresholds: {
        lines: 80,
        functions: 80,
        branches: 75,
        statements: 80,
      },
    },
  },
});
// test/setup.ts
import "@testing-library/jest-dom/vitest";

O Vitest documenta os providers v8 e istanbul. Em projetos Node ou Chromium, v8 costuma ser a escolha mais simples.

Exemplo 1: teste unitário de preço

// src/lib/pricing.ts
export type PriceInput = {
  unitPrice: number;
  quantity: number;
  discountRate?: number;
  taxRate?: number;
};

export function calculateTotal({
  unitPrice,
  quantity,
  discountRate = 0,
  taxRate = 0.1,
}: PriceInput): number {
  if (!Number.isInteger(quantity) || quantity < 0) {
    throw new Error("quantity must be a non-negative integer");
  }
  if (unitPrice < 0) throw new Error("unitPrice must be non-negative");
  if (discountRate < 0 || discountRate > 1) {
    throw new Error("discountRate must be between 0 and 1");
  }
  const discounted = unitPrice * quantity * (1 - discountRate);
  return Math.round(discounted * (1 + taxRate));
}
// src/lib/pricing.test.ts
import { describe, expect, it } from "vitest";
import { calculateTotal } from "./pricing";

describe("calculateTotal", () => {
  it("calculates a tax-included total", () => {
    expect(calculateTotal({ unitPrice: 1000, quantity: 2 })).toBe(2200);
  });

  it("applies discount before tax", () => {
    expect(
      calculateTotal({ unitPrice: 1000, quantity: 2, discountRate: 0.2 })
    ).toBe(1760);
  });

  it("allows zero quantity", () => {
    expect(calculateTotal({ unitPrice: 1000, quantity: 0 })).toBe(0);
  });

  it("rejects invalid inputs", () => {
    expect(() => calculateTotal({ unitPrice: 1000, quantity: -1 })).toThrow(
      "quantity must be a non-negative integer"
    );
  });
});

O erro comum é buscar 80% de cobertura apenas com caminhos felizes. Em preço, os casos importantes são quantidade zero, desconto total, taxas inválidas e arredondamento.

Exemplo 2: CTA com Testing Library

Testing Library recomenda queries próximas de como usuários encontram elementos. getByRole e getByLabelText são mais estáveis que classes CSS.

// src/components/CheckoutButton.tsx
import { useState } from "react";

type Props = {
  stock: number;
  onCheckout: () => Promise<void>;
};

export function CheckoutButton({ stock, onCheckout }: Props) {
  const [submitting, setSubmitting] = useState(false);
  const soldOut = stock <= 0;

  async function handleClick() {
    setSubmitting(true);
    try {
      await onCheckout();
    } finally {
      setSubmitting(false);
    }
  }

  return (
    <button
      type="button"
      disabled={soldOut || submitting}
      aria-busy={submitting}
      onClick={handleClick}
    >
      {soldOut ? "Sold out" : submitting ? "Processing..." : "Buy now"}
    </button>
  );
}
// src/components/CheckoutButton.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi } from "vitest";
import { CheckoutButton } from "./CheckoutButton";

describe("CheckoutButton", () => {
  it("calls checkout when in stock", async () => {
    const user = userEvent.setup();
    const onCheckout = vi.fn().mockResolvedValue(undefined);

    render(<CheckoutButton stock={3} onCheckout={onCheckout} />);
    await user.click(screen.getByRole("button", { name: "Buy now" }));

    expect(onCheckout).toHaveBeenCalledTimes(1);
  });

  it("prevents checkout when sold out", async () => {
    const user = userEvent.setup();
    const onCheckout = vi.fn().mockResolvedValue(undefined);

    render(<CheckoutButton stock={0} onCheckout={onCheckout} />);
    const button = screen.getByRole("button", { name: "Sold out" });

    expect(button).toBeDisabled();
    await user.click(button);
    expect(onCheckout).not.toHaveBeenCalled();
  });
});

Um CTA de receita não deve depender de .primary-button nem de snapshot como verificação principal. O teste precisa confirmar papel, texto e comportamento.

Exemplo 3: E2E pequeno com Playwright

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

const baseURL =
  process.env.PLAYWRIGHT_TEST_BASE_URL ?? "http://127.0.0.1:5173";

export default defineConfig({
  testDir: "./e2e",
  retries: process.env.CI ? 2 : 0,
  use: { baseURL, trace: "on-first-retry" },
  projects: [{ name: "chromium", use: { ...devices["Desktop Chrome"] } }],
  webServer: process.env.PLAYWRIGHT_TEST_BASE_URL
    ? undefined
    : {
        command: "npm run dev -- --host 127.0.0.1",
        url: baseURL,
        reuseExistingServer: !process.env.CI,
      },
});
// e2e/article-cta.spec.ts
import { expect, test } from "@playwright/test";

test("reader can move from article CTA to products", async ({ page }) => {
  await page.goto("/pt/blog/claude-code-testing-strategies");

  await page.getByRole("link", { name: /products|templates|produtos/i }).click();

  await expect(page).toHaveURL(/\/products\/?$/);
  await expect(page.getByRole("heading", { name: /products|produtos/i })).toBeVisible();
});

As assertions web-first do Playwright fazem retry automático. Prefira await expect(locator).toBeVisible() a esperas fixas. Evite caminhos CSS profundos e nth().

Exemplo 4: CI gate

# .github/workflows/test.yml
name: Test

on:
  pull_request:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
      - run: npm ci
      - run: npm run test:coverage
      - run: npx playwright install --with-deps
      - run: npm run test:e2e
        env:
          CI: "true"
      - uses: actions/upload-artifact@v5
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Se usar Claude Code GitHub Actions, siga a ação GA atual anthropics/claude-code-action@v1. Não copie exemplos antigos com @beta sem conferir a documentação.

Templates para Claude Code

Adicione testes unitários Vitest para src/lib/pricing.ts.
Cubra casos válidos, valores limite e entradas inválidas.
Não altere a implementação, a menos que primeiro liste o bug suspeito.
Execute npm run test -- pricing e resuma o resultado.
Adicione testes Testing Library para src/components/CheckoutButton.tsx.
Use getByRole e userEvent.setup().
Não use seletores CSS, snapshot como assertion principal nem detalhes internos.
Cubra em estoque, esgotado e estado de envio.
Adicione apenas um teste Playwright E2E para o fluxo CTA do artigo para produtos.
Evite waitForTimeout, seletores CSS profundos e nth().
Use assertions web-first e mantenha o teste focado na rota de receita.
Analise a falha mais recente da CI.
Classifique como lint, typecheck, unit, e2e ou ambiente.
Corrija a causa raiz com o menor diff.
Não pule testes nem apenas aumente timeouts.

Armadilhas comuns

Não faça mock de tudo. Gateways de pagamento e envio de e-mail podem ser simulados, mas preço, permissões e persistência crítica precisam ser testados na camada correta.

Não use E2E para congelar todos os detalhes visuais. E2E deve proteger cadastro, compra, contato e cliques em produtos.

Não trate cobertura como prova de qualidade. 80% ainda pode deixar fora a branch que cobra, apaga ou publica dados.

Não dê ao Claude Code apenas o caminho feliz. Inclua restrições: sem seletores frágeis, sem skip, sem snapshot-only e sem acoplamento a detalhes internos.

CTA

Testes também protegem caminhos de receita. Comece pela Claude Code cheatsheet, transforme regras repetidas em products and templates e use Claude Code training se o time precisar alinhar CI, review e responsabilidade por testes. Para continuar, veja TDD with Claude Code, CI/CD setup e debugging techniques.

Resultado prático

Masa testou este padrão nos CTAs de artigos e nos fluxos para produtos do ClaudeCodeLab. O melhor resultado não veio de adicionar muitos E2E, mas de manter preço em unit tests, comportamento do CTA em Testing Library e um único Playwright para o caminho artigo-produtos. Pedir ao Claude Code para listar modos de falha primeiro reduziu testes ignorados e seletores CSS frágeis. O risco restante está em pagamentos externos e scripts de anúncios, que ainda exigem evidência de CI e uma verificação manual curta antes da publicação.

#Claude Code #testing #test strategy #Vitest #Playwright
Grátis

PDF grátis: cheatsheet do Claude Code

Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.

Cuidamos dos seus dados e não enviamos spam.

Masa

Sobre o autor

Masa

Engenheiro focado em workflows práticos com Claude Code.