Testing: Jest, Vitest, Cypress & Playwright
Unit tests check small pieces: a price formatter, a Zod schema, a pure helper. They run fast and catch regressions early.
Unit testing overview
Unit tests check small pieces: a price formatter, a Zod schema, a pure helper. They run fast and catch regressions early.
Integration tests check a few units together. End-to-end (e2e) tests drive a real browser like a user.
Real-life example: Unit tests are tasting the sauce alone; e2e is eating the full plate at the restaurant table.
- Unit — functions and components in isolation
- Integration — page + mocked API
- E2E — full browser flows (login, checkout)
Jest and Vitest
Vitest is popular with Vite-style speed and Jest-compatible APIs. Jest remains common in many Next.js templates. Both work with React Testing Library.
Real-life example: Jest and Vitest are two brands of kitchen scale — same job (measure), slightly different buttons.
// lib/formatDuration.ts
export function formatDuration(mins: number) {
if (mins < 60) return `${mins} min`;
const h = Math.floor(mins / 60);
const m = mins % 60;
return m ? `${h}h ${m}m` : `${h}h`;
}
// formatDuration.test.ts
import { describe, it, expect } from "vitest";
import { formatDuration } from "./formatDuration";
describe("formatDuration", () => {
it("formats under one hour", () => {
expect(formatDuration(18)).toBe("18 min");
});
it("formats hours and minutes", () => {
expect(formatDuration(90)).toBe("1h 30m");
});
});import { render, screen } from "@testing-library/react";
import { CourseCard } from "./CourseCard";
it("shows the course title", () => {
render(<CourseCard title="Next.js Auth" duration="20 min" />);
expect(screen.getByText("Next.js Auth")).toBeInTheDocument();
});Cypress and Playwright (e2e)
Playwright and Cypress click through your running app. Prefer Playwright for modern multi-browser CI; Cypress is still widely taught.
Test happy paths first: home loads, login redirects, lesson page shows heading.
Real-life example: E2E tests are a dress rehearsal — lights, costume, full stage — before opening night (production).
import { test, expect } from "@playwright/test";
test("home page shows brand", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
});
test("learn page lists courses", async ({ page }) => {
await page.goto("/learn");
await expect(page.getByText(/Next\.js/i)).toBeVisible();
});describe("home", () => {
it("loads", () => {
cy.visit("/");
cy.get("h1").should("be.visible");
});
});