Advanced (Actualizado: 2/6/2026)

Estrategia de pruebas con Claude Code: Vitest, Testing Library, Playwright y CI

Diseña pruebas unitarias, de integración, E2E y CI con Claude Code sin crear tests frágiles.

Estrategia de pruebas con Claude Code: Vitest, Testing Library, Playwright y CI

Una estrategia de pruebas no consiste en escribir más tests. Consiste en decidir qué comportamiento debe protegerse con pruebas unitarias rápidas, qué límites necesitan pruebas de integración y qué flujos de negocio merecen una prueba E2E.

Si le pides a Claude Code “agrega tests” sin contexto, puede generar assertions superficiales, selectores CSS frágiles o pruebas que solo reflejan la implementación actual. Una petición mejor define la capa de prueba, el comportamiento esperado, los comandos a ejecutar y los límites de edición.

Esta guía se revisó contra la documentación oficial de Claude Code common workflows, Vitest coverage, Testing Library queries y Playwright para locators, assertions y CI.

Empieza con la pirámide

flowchart TB
  E2E["E2E: registro, checkout, CTA de ingresos"]
  INT["Integración: API, DB, formularios, componentes"]
  UNIT["Unidad: cálculo, validación, permisos"]
  E2E --> INT --> UNIT
CapaPorcentaje guíaQué protegeHerramientas
Unitaria60-70%lógica pura, validación, permisosVitest
Integración20-30%componentes, API, límites de DBVitest + Testing Library
E2E5-10%registro, compra, rutas de ingresosPlaywright
Puerta CIcada PRlint, tipos, pruebas, reportesGitHub Actions

La lógica de precios va en pruebas unitarias. Un botón de compra va en integración. El paso desde un CTA del artículo hasta la página de productos es un buen candidato para E2E.

Configuración 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";

Vitest documenta v8 e istanbul como proveedores de cobertura. Para proyectos Node o Chromium, v8 suele ser la opción más directa.

Ejemplo 1: Prueba unitaria de precios

// 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"
    );
  });
});

El error común es perseguir 80% de cobertura solo con casos felices. En precios importan más los límites: cantidad cero, descuentos extremos, tasas inválidas y redondeo.

Ejemplo 2: CTA con Testing Library

Testing Library recomienda consultas parecidas a la forma en que una persona encuentra elementos. Por eso getByRole y getByLabelText suelen ser mejores que selectores 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();
  });
});

No uses .primary-button ni snapshots como verificación principal de un CTA. Un CTA afecta ingresos, así que la prueba debe comprobar si el botón se encuentra, se puede usar y dispara el comportamiento correcto.

Ejemplo 3: E2E pequeño con 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("/es/blog/claude-code-testing-strategies");

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

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

Las assertions web-first de Playwright reintentan automáticamente. Prefiere await expect(locator).toBeVisible() en lugar de esperas fijas. Evita rutas CSS profundas y nth().

Ejemplo 4: Puerta de CI

# .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

Si usas Claude Code GitHub Actions, la documentación actual muestra anthropics/claude-code-action@v1 como acción GA. No copies ejemplos antiguos con @beta sin verificar.

Plantillas para Claude Code

Agrega pruebas unitarias con Vitest para src/lib/pricing.ts.
Cubre casos correctos, valores límite y entradas inválidas.
No cambies la implementación salvo que primero listes el posible bug.
Ejecuta npm run test -- pricing y resume el resultado.
Agrega pruebas de Testing Library para src/components/CheckoutButton.tsx.
Usa getByRole y userEvent.setup().
No uses selectores CSS, snapshots como assertion principal ni detalles internos.
Cubre stock disponible, agotado y estado de envío.
Agrega una sola prueba E2E con Playwright para el flujo CTA del artículo a productos.
Evita waitForTimeout, selectores CSS profundos y nth().
Usa assertions web-first y mantén la prueba centrada en la ruta de ingresos.
Revisa el fallo más reciente de CI.
Clasifícalo como lint, typecheck, unit, e2e o entorno.
Corrige la causa raíz con el diff más pequeño.
No omitas pruebas ni aumentes solo timeouts.

Errores frecuentes

No lo mocks todo. Mockea pasarelas de pago y envío de correo, pero prueba precios, permisos y persistencia en la capa correcta.

No uses E2E para congelar todos los detalles visuales. E2E debe proteger acciones de lector, registro, compra y contacto.

No tomes la cobertura como prueba de calidad. 80% puede ignorar justo la rama que cobra, elimina o publica datos.

No le des a Claude Code solo el caso feliz. Incluye prohibiciones: sin selectores frágiles, sin skip, sin snapshots-only y sin acoplarse a detalles internos.

CTA

Las pruebas protegen también las rutas de ingresos. Empieza con la Claude Code cheatsheet, convierte las revisiones repetidas en products and templates y usa Claude Code training si tu equipo necesita CI, reglas de review y propiedad de tests. Para profundizar, lee TDD with Claude Code, CI/CD setup y debugging techniques.

Resultado de la prueba práctica

Masa probó este patrón en los CTA de artículos y flujos hacia productos de ClaudeCodeLab. Lo más útil no fue añadir muchos E2E, sino dejar la lógica de precios en unit tests, el comportamiento del CTA en Testing Library y un solo Playwright para la ruta artículo-productos. Pedir a Claude Code que listara primero los modos de fallo redujo tests omitidos y selectores CSS frágiles. El riesgo restante son pagos externos y scripts de anuncios, que aún necesitan evidencia de CI y una revisión manual breve antes de publicar.

#Claude Code #testing #test strategy #Vitest #Playwright
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.