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, logica) y Playwright para E2E que simulan usuarios reales en un navegador. Con ambos cubres lo que importa sin complicarte.
Esta guia cubre el setup de los dos, ejemplos practicos, y como integrarlos en CI/CD.
Vitest: setup y unit tests
npm install -D vitest @vitejs/plugin-react @testing-library/react @testing-library/jest-dom jsdomConfiguracion minima:
// 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, ".") },
},
});// vitest.setup.ts
import "@testing-library/jest-dom/vitest";Agrega el script en package.json:
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}Test de una funcion
// 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 dolares", () => {
expect(formatPrice(1999)).toBe("$19.99");
});
it("maneja cero", () => {
expect(formatPrice(0)).toBe("$0.00");
});
});Test de un componente
// 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
// 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 validos", () => {
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
npm init playwright@latestEsto crea la config y descarga los navegadores. Configuracion basica:
// 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 navegacion
// e2e/navigation.spec.ts
import { test, expect } from "@playwright/test";
test("la pagina 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
// 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:
# .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 testSi ya tienes CI/CD configurado, revisa la guia de GitHub Actions para Next.js para integrar los tests en tu pipeline existente.
Que testear (y que no)
| Testear | No testear |
|---|---|
| Logica de negocio (validaciones, transformaciones) | Estilos CSS |
| Componentes con logica condicional | Componentes que solo renderizan props |
| Flujos criticos E2E (login, checkout) | Cada pagina individualmente |
| Schemas de Zod | Configuracion de Next.js |
| API routes con logica compleja | Librerias de terceros |
Siguiente paso
Los tests son parte clave de tu pipeline de CI/CD. La guia de CI/CD con GitHub Actions cubre como automatizar todo: lint, tests, build y deploy con cada push.
Preguntas frecuentes
Vitest o Jest para Next.js?
Vitest. Es mas rapido, compatible con ESM nativamente, y la configuracion es minima si ya usas Vite o Next.js. Jest funciona pero requiere mas config con TypeScript y ESM.
Que son tests E2E y por que 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 mas rapida y confiable para esto. Soporta Chrome, Firefox y Safari.
Cuantos tests necesito?
Depende del proyecto. Como minimo: unit tests para tu logica de negocio (validaciones, transformaciones) y E2E para los flujos criticos (login, checkout, formularios principales). No necesitas 100% de coverage para empezar.
Como 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 guia de CI/CD con GitHub Actions cubre el setup completo.
Articulos relacionados
Next.js 16: Guia de Migracion y Novedades
Migra tu proyecto de Next.js 15 a 16. Novedades principales, breaking changes, y pasos para actualizar sin romper tu app.
Tailwind CSS 4: Migracion desde v3
Migra tu proyecto de Tailwind CSS 3 a 4. Cambios principales, nuevo sistema de configuracion, CSS-first config y como actualizar sin romper tu app.
React Server Components: Guia Practica y Patrones
Guia practica de React Server Components. Cuando usarlos, patrones de composicion, data fetching, y como combinar Server y Client Components en Next.js.