Тестирование

Примеры

Узнайте, как настроить Next.js с популярными инструментами тестирования: Cypress, Playwright и Jest с React Testing Library.

Cypress

Cypress — это инструмент для выполнения сквозных (E2E) и компонентных тестов.

Быстрый старт

Вы можете использовать create-next-app с примером with-cypress для быстрого начала работы.

Терминал
npx create-next-app@latest --example with-cypress with-cypress-app

Ручная настройка

Для начала работы с Cypress установите пакет cypress:

Терминал
npm install --save-dev cypress

Добавьте Cypress в поле scripts файла package.json:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "cypress:open": "cypress open"
  }
}

Запустите Cypress в первый раз, чтобы сгенерировать примеры с рекомендуемой структурой папок:

Терминал
npm run cypress:open

Вы можете изучить сгенерированные примеры и раздел Writing Your First Test документации Cypress, чтобы ознакомиться с инструментом.

Когда использовать E2E, а когда компонентные тесты?

В документации Cypress есть руководство о различиях между этими типами тестов и когда уместно использовать каждый из них.

Создание первого сквозного теста в Cypress

Предположим, у вас есть две страницы Next.js:

pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <h1>Главная страница</h1>
      <Link href="/about">О нас</Link>
    </nav>
  )
}
pages/about.js
export default function About() {
  return (
    <div>
      <h1>Страница "О нас"</h1>
      <Link href="/">Главная страница</Link>
    </div>
  )
}

Добавьте тест для проверки работы навигации:

cypress/e2e/app.cy.js
describe('Навигация', () => {
  it('должна переходить на страницу "О нас"', () => {
    // Начинаем с главной страницы
    cy.visit('http://localhost:3000/')

    // Находим ссылку с атрибутом href, содержащим "about", и кликаем по ней
    cy.get('a[href*="about"]').click()

    // Новый URL должен содержать "/about"
    cy.url().should('include', '/about')

    // Новая страница должна содержать заголовок h1 с текстом "About Page"
    cy.get('h1').contains('About Page')
  })
})

Вы можете использовать cy.visit("/") вместо cy.visit("http://localhost:3000/"), если добавите baseUrl: 'http://localhost:3000' в файл конфигурации cypress.config.js.

Создание первого компонентного теста в Cypress

Компонентные тесты собирают и монтируют конкретный компонент без необходимости сборки всего приложения или запуска сервера. Это позволяет выполнять более производительные тесты, которые по-прежнему дают визуальную обратную связь и используют тот же API, что и сквозные тесты Cypress.

Полезно знать: Поскольку компонентные тесты не запускают сервер Next.js, такие возможности, как <Image /> и getServerSideProps, которые зависят от наличия сервера, не будут работать "из коробки". См. документацию Cypress по Next.js для примеров настройки этих функций в компонентных тестах.

Предположим, у вас те же компоненты, что и в предыдущем разделе. Добавьте тест для проверки рендеринга компонента:

pages/about.cy.js
import AboutPage from './about.js'

describe('<AboutPage />', () => {
  it('должен рендериться и отображать ожидаемый контент', () => {
    // Монтируем React-компонент страницы "О нас"
    cy.mount(<AboutPage />)

    // Страница должна содержать заголовок h1 с текстом "About Page"
    cy.get('h1').contains('About Page')

    // Проверяем наличие ссылки с ожидаемым URL
    // Переход по ссылке лучше тестировать в сквозном тесте
    cy.get('a[href="/"]').should('be.visible')
  })
})

Запуск тестов Cypress

Сквозные тесты

Поскольку сквозные тесты Cypress тестируют реальное приложение Next.js, перед запуском Cypress необходимо запустить сервер Next.js. Рекомендуется запускать тесты против production-сборки, чтобы поведение максимально соответствовало реальному.

Выполните npm run build и npm run start, затем в другом окне терминала запустите npm run cypress -- --e2e для запуска Cypress и выполнения сквозных тестов.

Полезно знать: Альтернативно можно установить пакет start-server-and-test и добавить его в поле scripts файла package.json: "test": "start-server-and-test start http://localhost:3000 cypress" для одновременного запуска production-сервера Next.js и Cypress. Не забудьте пересобрать приложение после внесения изменений.

Компонентные тесты

Запустите npm run cypress -- --component для запуска Cypress и выполнения компонентных тестов.

Подготовка к Continuous Integration (CI)

Вы могли заметить, что до сих пор Cypress запускался в интерактивном браузере, что не идеально для CI-окружений. Cypress также можно запускать в headless-режиме с помощью команды cypress run:

package.json
{
  "scripts": {
    //...
    "e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
    "e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
    "component": "cypress open --component",
    "component:headless": "cypress run --component"
  }
}

Подробнее о Cypress и Continuous Integration:

Playwright

Playwright — это фреймворк для тестирования, позволяющий автоматизировать Chromium, Firefox и WebKit с единым API. С его помощью можно писать сквозные (E2E) и интеграционные тесты для всех платформ.

Быстрый старт

Самый быстрый способ начать — использовать create-next-app с примером with-playwright. Это создаст проект Next.js с уже настроенным Playwright.

Терминал
npx create-next-app@latest --example with-playwright with-playwright-app

Ручная настройка

Вы также можете использовать npm init playwright для добавления Playwright в существующий проект NPM.

Для ручной настройки Playwright установите пакет @playwright/test:

Терминал
npm install --save-dev @playwright/test

Добавьте Playwright в поле scripts файла package.json:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test:e2e": "playwright test"
  }
}

Создание первого сквозного теста в Playwright

Предположим, у вас есть две страницы Next.js:

pages/index.js
import Link from 'next/link'

export default function Home() {
  return (
    <nav>
      <Link href="/about">О нас</Link>
    </nav>
  )
}
pages/about.js
export default function About() {
  return (
    <div>
      <h1>Страница "О нас"</h1>
    </div>
  )
}

Добавьте тест для проверки работы навигации:

import { test, expect } from '@playwright/test'

test('должен переходить на страницу "О нас"', async ({ page }) => {
  // Начинаем с главной страницы (baseURL задаётся через webServer в playwright.config.ts)
  await page.goto('http://localhost:3000/')
  // Находим элемент с текстом 'About Page' и кликаем по нему
  await page.click('text=About')
  // Новый URL должен быть "/about" (baseURL используется здесь)
  await expect(page).toHaveURL('http://localhost:3000/about')
  // Новая страница должна содержать заголовок h1 с текстом "About Page"
  await expect(page.locator('h1')).toContainText('About Page')
})
import { test, expect } from '@playwright/test'

test('должен переходить на страницу "О нас"', async ({ page }) => {
  // Начинаем с главной страницы (baseURL задаётся через webServer в playwright.config.ts)
  await page.goto('http://localhost:3000/')
  // Находим элемент с текстом 'About Page' и кликаем по нему
  await page.click('text=About')
  // Новый URL должен быть "/about" (baseURL используется здесь)
  await expect(page).toHaveURL('http://localhost:3000/about')
  // Новая страница должна содержать заголовок h1 с текстом "About Page"
  await expect(page.locator('h1')).toContainText('About Page')
})

Вы можете использовать page.goto("/") вместо page.goto("http://localhost:3000/"), если добавите "baseURL": "http://localhost:3000" в файл конфигурации playwright.config.ts.

Запуск тестов Playwright

Поскольку Playwright тестирует реальное приложение Next.js, перед запуском тестов необходимо запустить сервер Next.js. Рекомендуется запускать тесты против production-сборки, чтобы поведение максимально соответствовало реальному.

Выполните npm run build и npm run start, затем в другом окне терминала запустите npm run test:e2e для выполнения тестов Playwright.

Полезно знать: Альтернативно можно использовать функцию webServer, чтобы Playwright сам запускал development-сервер и ждал его готовности.

Запуск Playwright в Continuous Integration (CI)

По умолчанию Playwright запускает тесты в headless-режиме. Для установки всех зависимостей Playwright выполните npx playwright install-deps.

Подробнее о Playwright и Continuous Integration:

Jest и React Testing Library

Jest и React Testing Library часто используются вместе для модульного тестирования. Есть три способа начать использовать Jest в вашем приложении Next.js:

  1. Используя один из наших примеров для быстрого старта
  2. С Rust-компилятором Next.js
  3. С Babel

В следующих разделах описана настройка Jest для каждого из этих вариантов:

Быстрый старт

Вы можете использовать create-next-app с примером with-jest для быстрого начала работы с Jest и React Testing Library:

Терминал
npx create-next-app@latest --example with-jest with-jest-app

Настройка Jest (с Rust-компилятором)

Начиная с Next.js 12, Next.js имеет встроенную конфигурацию для Jest.

Для настройки Jest установите jest, jest-environment-jsdom, @testing-library/react, @testing-library/jest-dom:

Терминал
npm install --save-dev jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom

Создайте файл jest.config.mjs в корневой директории вашего проекта и добавьте следующее:

jest.config.mjs
import nextJest from 'next/jest.js'

const createJestConfig = nextJest({
  // Укажите путь к вашему приложению Next.js для загрузки next.config.js и .env файлов в тестовом окружении
  dir: './',
})

// Добавьте любую пользовательскую конфигурацию для передачи в Jest
/** @type {import('jest').Config} */
const config = {
  // Дополнительные настройки перед запуском каждого теста
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],

  testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig экспортируется таким образом, чтобы next/jest мог загрузить асинхронную конфигурацию Next.js
export default createJestConfig(config)

Под капотом next/jest автоматически настраивает Jest, включая:

  • Настройку transform с использованием SWC
  • Автоматические моки для стилей (.css, .module.css и их scss-вариантов), импортов изображений и next/font
  • Загрузку .env (и всех вариантов) в process.env
  • Игнорирование node_modules при разрешении и трансформации тестов
  • Игнорирование .next при разрешении тестов
  • Загрузку next.config.js для флагов, включающих трансформации SWC

Полезно знать: Для тестирования переменных окружения напрямую загружайте их вручную в отдельном скрипте настройки или в файле jest.config.js. Подробнее см. Тестовые переменные окружения.

Настройка Jest (с Babel)

Если вы отказываетесь от использования Rust-компилятора, вам потребуется вручную настроить Jest и установить babel-jest и identity-obj-proxy в дополнение к перечисленным выше пакетам.

Рекомендуемые параметры для настройки Jest в Next.js:

jest.config.js
module.exports = {
  collectCoverage: true,
  // в node 14.x провайдер покрытия v8 обеспечивает хорошую скорость и более-менее качественный отчёт
  coverageProvider: 'v8',
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**',
    '!<rootDir>/out/**',
    '!<rootDir>/.next/**',
    '!<rootDir>/*.config.js',
    '!<rootDir>/coverage/**',
  ],
  moduleNameMapper: {
    // Обработка импортов CSS (с CSS-модулями)
    // https://jestjs.io/docs/webpack#mocking-css-modules
    '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy',

    // Обработка импортов CSS (без CSS-модулей)
    '^.+\\.(css|sass|scss)$': '<rootDir>/__mocks__/styleMock.js',

    // Обработка импортов изображений
    // https://jestjs.io/docs/webpack#handling-static-assets
    '^.+\\.(png|jpg|jpeg|gif|webp|avif|ico|bmp|svg)$/i': `<rootDir>/__mocks__/fileMock.js`,

    // Обработка алиасов модулей
    '^@/components/(.*)$': '<rootDir>/components/$1',
  },
  // Добавьте дополнительные настройки перед запуском каждого теста
  // setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  testPathIgnorePatterns: ['<rootDir>/node_modules/', '<rootDir>/.next/'],
  testEnvironment: 'jsdom',
  transform: {
    // Используйте babel-jest для транспиляции тестов с пресетом next/babel
    // https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }],
  },
  transformIgnorePatterns: [
    '/node_modules/',
    '^.+\\.module\\.(css|sass|scss)$',
  ],
}

Подробнее о каждом параметре конфигурации можно узнать в документации Jest.

Обработка таблиц стилей и импортов изображений

Таблицы стилей и изображения не используются в тестах, но их импорт может вызывать ошибки, поэтому их необходимо замокать. Создайте файлы-моки, указанные в конфигурации выше — fileMock.js и styleMock.js — внутри директории __mocks__:

__mocks__/fileMock.js
module.exports = {
  src: '/img.jpg',
  height: 24,
  width: 24,
  blurDataURL: 'data:image/png;base64,imagedata',
}
__mocks__/styleMock.js
module.exports = {}

Подробнее о работе со статическими ресурсами см. в документации Jest.

Опционально: Расширение Jest пользовательскими матчерами

@testing-library/jest-dom включает набор удобных пользовательских матчеров, таких как .toBeInTheDocument(), что упрощает написание тестов. Вы можете импортировать эти матчеры для каждого теста, добавив следующую настройку в конфигурационный файл Jest:

jest.config.js
setupFilesAfterEnv: ['<rootDir>/jest.setup.js']

Затем внутри jest.setup.js добавьте следующий импорт:

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

extend-expect был удалён в v6.0, поэтому если вы используете @testing-library/jest-dom версии ниже 6, вам нужно импортировать @testing-library/jest-dom/extend-expect.

Если вам нужно добавить дополнительные настройки перед каждым тестом, их обычно добавляют в файл jest.setup.js, как показано выше.

Опционально: Абсолютные импорты и алиасы модулей

Если ваш проект использует алиасы модулей, вам нужно настроить Jest для их разрешения, сопоставив параметр paths в файле jsconfig.json с параметром moduleNameMapper в файле jest.config.js. Например:

tsconfig.json or jsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "@/components/*": ["components/*"]
    }
  }
}
jest.config.js
moduleNameMapper: {
  '^@/components/(.*)$': '<rootDir>/components/$1',
}

Создание тестов:

Добавление скрипта тестирования в package.json

Добавьте исполняемый файл Jest в режиме watch в скрипты package.json:

package.json
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "test": "jest --watch"
  }
}

jest --watch будет перезапускать тесты при изменении файлов. Подробнее о CLI-опциях Jest см. в документации Jest.

Создание первых тестов

Теперь ваш проект готов к запуску тестов. Следуя соглашениям Jest, добавляйте тесты в папку __tests__ в корневой директории проекта.

Например, можно добавить тест для проверки успешного рендеринга заголовка в компоненте <Home />:

__tests__/index.test.js
import { render, screen } from '@testing-library/react'
import Home from '../pages/index'
import '@testing-library/jest-dom'

describe('Home', () => {
  it('renders a heading', () => {
    render(<Home />)

    const heading = screen.getByRole('heading', {
      name: /welcome to next\.js!/i,
    })

    expect(heading).toBeInTheDocument()
  })
})

Опционально можно добавить снэпшот-тест для отслеживания неожиданных изменений в компоненте <Home />:

__tests__/snapshot.js
import { render } from '@testing-library/react'
import Home from '../pages/index'

it('renders homepage unchanged', () => {
  const { container } = render(<Home />)
  expect(container).toMatchSnapshot()
})

Полезно знать: Тестовые файлы не должны находиться внутри Pages Router, так как любые файлы внутри него считаются маршрутами.

Запуск тестового набора

Выполните npm run test для запуска тестового набора. После прохождения или провала тестов вы увидите список интерактивных команд Jest, которые помогут при добавлении новых тестов.

Для дальнейшего изучения могут быть полезны следующие ресурсы:

Пакеты и примеры от сообщества

Сообщество Next.js создало пакеты и статьи, которые могут быть полезны:

Для получения дополнительной информации рекомендуем:

  • pages/basic-features/environment-variables#test-environment-variables