guias·8 min de lectura

Testing en Next.js con Vitest y Playwright

Configura testing en tu proyecto Next.js. Unit tests con Vitest, E2E con Playwright, y como integrarlos en tu pipeline de CI/CD.

Testing en Next.js con Vitest y Playwright

Testing en Next.js se divide en dos: Vitest para unit tests rapidos (funciones, componentes, lógica) y Playwright para E2E que simulan usuarios reales en un navegador. Con ambos cubres lo que importa sin complicarte.

Esta guía cubre el setup de los dos, ejemplos prácticos, y como integrarlos en CI/CD.

Vitest: setup y unit tests

bash
npm install -D vitest @vitejs/plugin-react @testing-library/react @testing-library/jest-dom jsdom

Configuración mínima:

typescript
// vitest.config.ts
import { defineConfig } from "vitest/config";
import react from "@vitejs/plugin-react";
import path from "path";
 
export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    setupFiles: "./vitest.setup.ts",
    globals: true,
  },
  resolve: {
    alias: { "@": path.resolve(__dirname, ".") },
  },
});
typescript
// vitest.setup.ts
import "@testing-library/jest-dom/vitest";

Agrega el script en package.json:

json
{
  "scripts": {
    "test": "vitest",
    "test:run": "vitest run"
  }
}

Test de una función

typescript
// lib/__tests__/format.test.ts
import { describe, it, expect } from "vitest";
 
function formatPrice(cents: number): string {
  return `$${(cents / 100).toFixed(2)}`;
}
 
describe("formatPrice", () => {
  it("formatea centavos a dólares", () => {
    expect(formatPrice(1999)).toBe("$19.99");
  });
 
  it("maneja cero", () => {
    expect(formatPrice(0)).toBe("$0.00");
  });
});

Test de un componente

typescript
// components/__tests__/Button.test.tsx
import { render, screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
 
function Button({ onClick, children }: {
  onClick: () => void;
  children: React.ReactNode;
}) {
  return <button onClick={onClick}>{children}</button>;
}
 
describe("Button", () => {
  it("renderiza el texto", () => {
    render(<Button onClick={() => {}}>Enviar</Button>);
    expect(screen.getByText("Enviar")).toBeInTheDocument();
  });
 
  it("llama onClick al hacer clic", () => {
    const handleClick = vi.fn();
    render(<Button onClick={handleClick}>Enviar</Button>);
    fireEvent.click(screen.getByText("Enviar"));
    expect(handleClick).toHaveBeenCalledOnce();
  });
});

Test de un schema Zod

typescript
// lib/__tests__/schemas.test.ts
import { describe, it, expect } from "vitest";
import { z } from "zod";
 
const userSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
});
 
describe("userSchema", () => {
  it("acepta datos válidos", () => {
    const result = userSchema.safeParse({
      email: "rod@ejemplo.com",
      name: "Rod",
    });
    expect(result.success).toBe(true);
  });
 
  it("rechaza email invalido", () => {
    const result = userSchema.safeParse({
      email: "no-es-email",
      name: "Rod",
    });
    expect(result.success).toBe(false);
  });
});

Playwright: tests E2E

bash
npm init playwright@latest

Esto crea la config y descarga los navegadores. Configuración básica:

typescript
// playwright.config.ts
import { defineConfig } from "@playwright/test";
 
export default defineConfig({
  testDir: "./e2e",
  webServer: {
    command: "npm run dev",
    url: "http://localhost:3000",
    reuseExistingServer: !process.env.CI,
  },
  use: {
    baseURL: "http://localhost:3000",
  },
});

Test de navegación

typescript
// e2e/navigation.spec.ts
import { test, expect } from "@playwright/test";
 
test("la página de inicio carga correctamente", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveTitle(/Rod Alexanderson/);
});
 
test("navegar al blog muestra posts", async ({ page }) => {
  await page.goto("/blog");
  const posts = page.locator("article");
  await expect(posts.first()).toBeVisible();
});

Test de formulario

typescript
// e2e/contacto.spec.ts
import { test, expect } from "@playwright/test";
 
test("enviar formulario de contacto", async ({ page }) => {
  await page.goto("/contacto");
 
  await page.fill('input[name="nombre"]', "Rod");
  await page.fill('input[name="email"]', "rod@ejemplo.com");
  await page.fill('textarea[name="mensaje"]', "Hola, esto es un test");
  await page.click('button[type="submit"]');
 
  await expect(page.locator("text=Mensaje enviado")).toBeVisible();
});

Integrar en CI/CD

Agrega los tests a tu GitHub Actions workflow:

yaml
# .github/workflows/test.yml
name: Tests
on: [push, pull_request]
 
jobs:
  unit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npm run test:run
 
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test

Si ya tienes CI/CD configurado, revisa la guía de GitHub Actions para Next.js para integrar los tests en tu pipeline existente.

Que testear (y que no)

TestearNo testear
lógica de negocio (validaciones, transformaciones)Estilos CSS
Componentes con lógica condicionalComponentes que solo renderizan props
Flujos críticos E2E (login, checkout)Cada página individualmente
Schemas de ZodConfiguración de Next.js
API routes con lógica complejalibrerías de terceros

Siguiente paso

Los tests son parte clave de tu pipeline de CI/CD. La guía de CI/CD con GitHub Actions cubre como automatizar todo: lint, tests, build y deploy con cada push.

#testing#vitest#playwright#nextjs#ci-cd

Preguntas frecuentes

¿Vitest o Jest para Next.js?

Vitest. Es más rápido, compatible con ESM nativamente, y la configuración es mínima si ya usas Vite o Next.js. Jest funciona pero requiere más config con TypeScript y ESM.

¿Qué son tests E2E y por qué necesito Playwright?

Tests end-to-end (E2E) simulan un usuario real: abren el navegador, hacen clic, llenan formularios y verifican que todo funcione. Playwright es la herramienta más rápida y confiable para esto. Soporta Chrome, Firefox y Safari.

¿Cuántos tests necesito?

Depende del proyecto. Como mínimo: unit tests para tu lógica de negocio (validaciones, transformaciones) y E2E para los flujos críticos (login, checkout, formularios principales). No necesitas 100% de coverage para empezar.

¿Cómo corro tests en CI/CD?

Agrega un step en tu GitHub Actions workflow que corra npm test para Vitest y npx playwright test para E2E. La guía de CI/CD con GitHub Actions cubre el setup completo.