Next.js Testing

Overview

Testing is essential for ensuring code quality. Next.js supports multiple testing approaches, including unit tests, integration tests, and end-to-end tests.

Jest + React Testing Library

Installation

npm install -D jest @testing-library/react @testing-library/jest-dom jest-environment-jsdom

Configuration

// jest.config.js
const nextJest = require('next/jest')

const createJestConfig = nextJest({
  dir: './',
})

const customJestConfig = {
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testEnvironment: 'jest-environment-jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
  },
}

module.exports = createJestConfig(customJestConfig)

// jest.setup.js
import '@testing-library/jest-dom'

Component Tests

// components/Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react'
import Button from './Button'

describe('Button', () => {
  it('renders button text', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })
  
  it('calls onClick when clicked', () => {
    const handleClick = jest.fn()
    render(<Button onClick={handleClick}>Click me</Button>)
    fireEvent.click(screen.getByText('Click me'))
    expect(handleClick).toHaveBeenCalledTimes(1)
  })
})

Playwright (E2E)

Installation

npm install -D @playwright/test
npx playwright install

Configuration

// playwright.config.ts
import { defineConfig } from '@playwright/test'

export default defineConfig({
  testDir: './e2e',
  use: {
    baseURL: 'http://localhost:3000',
  },
  webServer: {
    command: 'npm run dev',
    port: 3000,
  },
})

E2E Tests

// e2e/home.spec.ts
import { test, expect } from '@playwright/test'

test('home page', async ({ page }) => {
  await page.goto('/')
  await expect(page.getByRole('heading', { name: '欢迎' })).toBeVisible()
})

test('navigation', async ({ page }) => {
  await page.goto('/')
  await page.click('text=关于')
  await expect(page).toHaveURL('/about')
})

API Testing

// app/api/users/route.test.ts
import { GET, POST } from './route'

describe('/api/users', () => {
  it('GET returns users', async () => {
    const response = await GET()
    const data = await response.json()
    expect(Array.isArray(data)).toBe(true)
  })
  
  it('POST creates user', async () => {
    const request = new Request('http://localhost:3000/api/users', {
      method: 'POST',
      body: JSON.stringify({ name: '张三', email: 'zhang@example.com' })
    })
    
    const response = await POST(request)
    const data = await response.json()
    expect(data.name).toBe('张三')
  })
})

Best Practices

1. Test Coverage

// package.json
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage"
  }
}

2. Mock Data

// __mocks__/api.ts
export const mockUser = {
  id: '1',
  name: '张三',
  email: 'zhang@example.com'
}

// components/UserProfile.test.tsx
jest.mock('@/lib/api')
import { mockUser } from '@/__mocks__/api'

test('displays user info', async () => {
  render(<UserProfile userId="1" />)
  expect(await screen.findByText(mockUser.name)).toBeInTheDocument()
})

3. Snapshot Testing

import { render } from '@testing-library/react'
import Component from './Component'

test('matches snapshot', () => {
  const { container } = render(<Component />)
  expect(container).toMatchSnapshot()
})

Previous Chapter: Next.js Internationalization (i18n) | Next Chapter: Next.js Performance Optimization