Technology · Next.js
Next.js Testing
Test Next.js apps with Jest, React Testing Library, and E2E testing with Playwright or Cypress.
TL;DR
- 01Use Jest for unit and integration tests of functions and components.
- 02Use React Testing Library to test components from the user perspective.
- 03Use Playwright or Cypress for end-to-end testing full user workflows.
Setup and Jest Basics
- Jest is pre-configured in Next.js, so start testing immediately.
npm test - Create test files with
.test.jsor.spec.jsextensions.// utils.test.js import { add } from "./utils"; describe("add function", () => { it("adds two numbers", () => { expect(add(2, 3)).toBe(5); }); }); - Run tests in watch mode to re-run on file changes.
npm test -- --watch - Use
describeto group related tests anditfor individual test cases. - Jest runs tests in parallel by default for speed.
Testing Components
- Import React Testing Library to test components from user perspective.
import { render, screen } from "@testing-library/react"; import Button from "./Button"; it("renders a button", () => { render(<Button>Click me</Button>); const btn = screen.getByRole("button", { name: /click me/i }); expect(btn).toBeInTheDocument(); }); - Query elements using semantic methods like
getByRole,getByText.screen.getByRole("button", { name: /submit/i }); screen.getByLabelText("Email"); screen.getByText("Welcome"); - Test user interactions with
userEventinstead offireEvent.import userEvent from "@testing-library/user-event"; const user = userEvent.setup(); await user.click(button); - Avoid testing implementation details, focus on what users see and do.
Testing API Routes
- Create separate test files for API route handlers.
import { createMocks } from "node-mocks-http"; import handler from "./api/hello"; it("returns a greeting", async () => { const { req, res } = createMocks({ method: "GET" }); await handler(req, res); expect(res._getStatusCode()).toBe(200); expect(JSON.parse(res._getData())).toHaveProperty("message"); }); - Use a library like
node-mocks-httpto mock request and response. - Test different HTTP methods and status codes.
- Mock dependencies like databases or external APIs.
Mocking and Fixtures
- Mock external API calls to keep tests fast and isolated.
jest.mock("./api", () => ({ fetchUser: jest.fn(() => Promise.resolve({ id: 1, name: "Alice" })) })); - Use fixtures to provide consistent test data.
const mockUser = { id: 1, name: "Alice", email: "alice@example.com" }; it("displays user info", () => { render(<Profile user={mockUser} />); expect(screen.getByText("Alice")).toBeInTheDocument(); }); - Mock Next.js router for navigation testing.
jest.mock("next/router", () => ({ useRouter: jest.fn(() => ({ push: jest.fn() })) })); - Mocking prevents external dependencies from slowing tests.
E2E Testing
- Use Playwright or Cypress for end-to-end testing of full workflows.
// playwright.spec.ts import { test, expect } from "@playwright/test"; test("user can sign up", async ({ page }) => { await page.goto("http://localhost:3000"); await page.fill("input[name=email]", "test@example.com"); await page.click("button:has-text('Sign Up')"); await expect(page).toHaveURL("/dashboard"); }); - E2E tests run in a real browser and test the entire application.
- Run E2E tests against a deployed environment before releasing.
- E2E tests are slower but catch integration issues that unit tests miss.
npx playwright test - Use E2E tests for critical user paths like checkout or login.
Tip: Aim for a test pyramid: many unit tests, some integration tests, and a few critical E2E tests to balance speed and confidence.
Warning: Avoid testing implementation details and focus on user behavior, since refactored code will break tests that depend on internal structure.