LearnAI ToolsCareerPractice BuildsPlayContact
Lesson 4617 min read

Testing Next.js Applications

Set up unit tests with Vitest/Jest and end-to-end tests with Playwright for a Next.js project.

Levels of Testing

Unit Tests

Test a single function or component in isolation — fast, focused, and cheap to run.

Integration Tests

Test how several pieces work together, like a form calling a Server Action.

End-to-End (E2E) Tests

Drive a real browser through your app exactly as a user would.

Unit Testing Components

Vitest (or Jest) combined with React Testing Library is the standard setup for testing individual components.

npm install --save-dev vitest @testing-library/react @testing-library/jest-dom jsdom
// Card.test.tsx
import { render, screen } from '@testing-library/react';
import Card from './Card';
test('renders the card title', () => {
render(<Card title="Hello">Content</Card>);
expect(screen.getByText('Hello')).toBeInTheDocument();
});
Server Components Aren't Directly Unit-Testable Yet

Because Server Components are async and render on the server, tooling for unit-testing them directly is still evolving — most teams test the plain functions they call, and lean on end-to-end tests for full-page behavior.

Testing Server Actions

Since a Server Action is just an async function, you can often test its logic directly by importing and calling it with mocked inputs, separate from any UI.

import { createPost } from './actions';
test('rejects a title that is too short', async () => {
const formData = new FormData();
formData.set('title', 'Hi');
const result = await createPost(null, formData);
expect(result.error).toBe('Title must be at least 3 characters.');
});

End-to-End Testing with Playwright

Playwright drives a real browser against your running app, verifying full user flows — navigation, form submission, authentication — the way a real visitor would experience them.

npm init playwright@latest
import { test, expect } from '@playwright/test';
test('user can create a post', async ({ page }) => {
await page.goto('/blog/new');
await page.fill('input[name="title"]', 'My First Post');
await page.click('button[type="submit"]');
await expect(page.getByText('My First Post')).toBeVisible();
});

FAQs

For most real projects, yes — unit tests catch logic bugs quickly and cheaply, while end-to-end tests catch integration issues that only show up in a real browser.

Indirectly, yes — by triggering the form or button that calls the Server Action and asserting on the resulting UI, exactly as a real user's interaction would.

Summary

Unit tests, Server Action tests, and Playwright end-to-end tests together give solid coverage across a Next.js app's logic and user flows. Next, you'll look specifically at performance optimization techniques.

Next Lesson →

Performance Optimization