Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Testing basics

An automated test is code that checks other code for you: it clicks a button and compares the result with what is expected. A Next.js app usually has two kinds of tests: unit tests check a single component or function, end-to-end (E2E) tests open the real site in a browser and walk through a whole scenario.

Installing Vitest

Vitest is a fast tool for unit tests; the Testing Library package is installed with it to render components:

npm install -D vitest @vitejs/plugin-react jsdom @testing-library/react @testing-library/dom vite-tsconfig-paths

Create a settings file in the project root:

// vitest.config.mts
import react from '@vitejs/plugin-react';
import tsconfigPaths from 'vite-tsconfig-paths';
import { defineConfig } from 'vitest/config';
 
export default defineConfig({
  plugins: [tsconfigPaths(), react()],
  test: { environment: 'jsdom' },
});

jsdom imitates a browser inside Node.js, and tsconfigPaths teaches Vitest to understand paths like @/lib/posts. Add a "test": "vitest" script to package.json.

A component test

Let us test the Like button from the client components lesson. The test file sits next to the component:

// components/LikeButton.test.tsx
import { fireEvent, render, screen } from '@testing-library/react';
import { expect, test } from 'vitest';
import LikeButton from './LikeButton';
 
test('the counter grows on click', () => {
  render(<LikeButton />);
  const button = screen.getByRole('button');
 
  fireEvent.click(button);
 
  expect(button.textContent).toBe('Likes: 1');
});

render draws the component, screen.getByRole finds the button the way a user would, fireEvent.click clicks it. Run npm test — the terminal shows a green mark next to "the counter grows on click".

An end-to-end test in Playwright

Vitest cannot render asynchronous server components. They are checked with end-to-end tests in a real browser:

npm init playwright@latest

The command installs Playwright and its browsers and creates a folder for tests. A test for the blog:

// e2e/posts.spec.ts
import { expect, test } from '@playwright/test';
 
test('a post opens from the list', async ({ page }) => {
  await page.goto('http://localhost:3000/posts');
  await page.getByRole('link', { name: 'Hello, Next.js' }).click();
 
  await expect(page.getByRole('heading', { level: 1 })).toHaveText('Hello, Next.js');
});

Start the site with npm run dev, and in another terminal run npx playwright test. Playwright opens /posts, clicks the link and checks the post heading.

What to test with what

What to checkTool
A form validation function, calculationsVitest
A client componentVitest and Testing Library
A page with data, a form, navigationPlaywright

Common mistake

Writing a Vitest test for an async page, such as render(<PostsPage />). The test fails: Testing Library does not support asynchronous server components. Move the logic into ordinary functions and test them in Vitest, and check the page as a whole in Playwright.

Thanks for reading!

Donate to the site — we work hard for you.