Next.js Testing Basics
Every change to an application carries a small risk. A tweak to a price calculator might break the checkout page. A renamed prop might empty a product card. Automated tests catch these problems in seconds, before visitors ever see them. Testing turns fear of change into confidence.
The Everyday View
Think of a pilot's checklist. The pilot knows how to fly, yet a checklist confirms that fuel, brakes, and controls work before every takeoff. Automated tests serve as that checklist for your code. Each run confirms that the important parts still behave as expected.
Types of Tests
Three test types cover most needs. Each one trades speed against realism.
/\
/ \ End-to-end tests
/ \ (few, slow, realistic)
/------\
/ \ Integration tests
/ \ (some, medium)
/------------\
/ \ Unit tests
/ \ (many, fast, small)
/------------------\
Unit Tests
A unit test checks one small piece, such as a function that formats a price. These tests run in milliseconds and pinpoint failures precisely.
Integration Tests
An integration test checks several pieces together, such as a form component and its validation helper. The test confirms that the parts cooperate.
End-to-End Tests
An end-to-end test drives a real browser. The script opens the site, clicks buttons, types text, and checks the screen. These tests give the most realistic picture and take the longest time.
Common Tools
| Tool | Purpose |
|---|---|
| Jest or Vitest | Test runner for unit and integration tests |
| React Testing Library | Renders components and queries the screen the way a user sees it |
| Playwright or Cypress | Browser automation for end-to-end tests |
Next.js documentation provides setup guides for each tool. Install the packages that match your choice and follow the steps for your version.
A First Unit Test
Start with a plain function. This helper turns a number into a price label.
// lib/formatPrice.js
export function formatPrice(amount) {
return '$' + amount.toFixed(2);
}The matching test lives in a file that ends with .test.js.
// lib/formatPrice.test.js
import { describe, it, expect } from 'vitest';
import { formatPrice } from './formatPrice';
describe('formatPrice', () => {
it('adds a dollar sign and two decimals', () => {
expect(formatPrice(5)).toBe('$5.00');
});
it('rounds long decimals', () => {
expect(formatPrice(9.999)).toBe('$10.00');
});
});The describe block groups related checks. Each it block states one expected behavior in plain language. The expect call compares the real result with the target.
Testing a Component
React Testing Library renders a component inside a simulated page. Queries find elements by the text and roles that a visitor sees.
// components/Greeting.js
export default function Greeting({ name }) {
return <h1>Welcome, {name}</h1>;
}
// components/Greeting.test.js
import { render, screen } from '@testing-library/react';
import { it, expect } from 'vitest';
import Greeting from './Greeting';
it('shows the visitor name', () => {
render(<Greeting name="Asha" />);
expect(screen.getByText('Welcome, Asha')).toBeTruthy();
});Searching by visible text keeps tests close to real behavior. A rewrite of the internal code does not break the test as long as the screen output stays the same.
A First End-to-End Test
// e2e/home.spec.js
import { test, expect } from '@playwright/test';
test('home page shows the main heading', async ({ page }) => {
await page.goto('http://localhost:3000');
await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
});Start the dev server before the run, or configure the tool to start it automatically.
Async Server Components
Unit test tools struggle with async Server Components because those components run on the server and await data. The official guidance recommends end-to-end tests for such components. Move reusable logic into plain functions and test those functions directly.
Running Tests
Add a script to package.json so the whole team uses the same command.
{
"scripts": {
"test": "vitest",
"test:e2e": "playwright test"
}
}Run npm test during development and in your deployment pipeline. A failing test should stop a release.
What to Test First
- Price, tax, and discount calculations.
- Form validation rules.
- Sign-in and sign-out paths.
- The checkout or signup journey.
- Bugs you have fixed before, to prevent their return.
Good Testing Habits
- Name each test after the behavior, not the code.
- Keep each test focused on one idea.
- Avoid testing library internals.
- Run tests often so failures stay easy to trace.
- Fix or delete flaky tests quickly.
Common Mistakes
Chasing Full Coverage
A high percentage does not guarantee quality. Cover the paths that matter most to visitors.
Testing Everything in the Browser
Browser tests run slowly. Use them for key journeys and rely on unit tests for details.
Quick Summary
Unit tests check small pieces, integration tests check teamwork, and end-to-end tests check whole journeys. Start with a few tests on the most important logic and grow the suite as the app grows.
