Тестирование Next.js-приложения с Jest — пошаговое руководство

Введение
Главная цель любого процесса разработки — довести приложение до продакшен-готового состояния. Для этого код должен не только реализовывать бизнес‑логику, но и оставаться стабильным при дальнейших изменениях. Тесты помогают зафиксировать ожидаемое поведение и предотвратить регрессии.
Покрыть все возможные краевые случаи требует времени, но это окупается: баги выявляются раньше, а не в продакшене.
Тестирование Next.js-приложений
Писать тесты — важная и часто недооценённая часть разработки. Легко поддаться соблазну напрямую выкатывать изменения в продакшен, полагаясь на то, что «я писал этот код, значит он работает». Но такой подход ведёт к неожиданным сбоям.
Подход TDD (test-driven development) или просто регулярное добавление юнит‑ и интеграционных тестов значительно повышает уверенность в изменениях и экономит время на отладке.
Важно: тесты не заменяют E2E‑покрытие и наблюдаемость в продакшене, но они минимизируют тривиальные регрессии и облегчают рефакторинг.
Что такое Jest?
Jest — популярный фреймворк для тестирования JavaScript/TypeScript приложений. Он включает раннер тестов, систему моков и поддержку snapshot‑тестов. В паре с React Testing Library Jest позволяет писать понятные и стабильные тесты компонентов.
Кратко: Jest — это инструмент для запуска тестов, утверждений и моков; React Testing Library — для взаимодействия с компонентами как с пользователем.
Создание примера: To‑Do приложение на Next.js
Мы пройдём пример создания простого To‑Do и написания для него тестов. Предполагается, что у вас установлен Node.js и npm.
Шаги для запуска проекта и установки зависимостей:
- Создайте новый проект Next.js:
npx create-next-app@latest nextjs-test-tutorial- Перейдите в директорию проекта:
cd nextjs-test-tutorial- Установите dev‑зависимости:
npm install --save-dev jest @testing-library/react @testing-library/jest-dom jest-environment-jsdomПримечание: ставьте зависимости как devDependencies — они нужны только для тестирования.
После установки можно приступить к реализации компонента и тестов.
Создаём компонент To‑Do
В папке проекта откройте файл src/pages/index.js, удалите boilerplate и замените код на следующий. Сначала импортируем React‑хуки и стили, затем определим функции addTodo и deleteTodo.
import { useState } from "react";
import styles from "../styles/Home.module.css";
export default function Home() {
const [todos, setTodos] = useState([]);
const [newTodo, setNewTodo] = useState("");
const addTodo = () => {
if (newTodo.trim() !== "") {
const updatedTodos = [...todos, newTodo];
setTodos(updatedTodos);
setNewTodo("");
}
};
const deleteTodo = (index) => {
const updatedTodos = [...todos];
updatedTodos.splice(index, 1);
setTodos(updatedTodos);
};
return (
setNewTodo(e.target.value)}
/>
{todos.map((todo, index) => (
-
{todo}
))}
{todos.map((todo, index) => (
))}
);
}Пояснение: useState управляет массивом todos и текущим вводом newTodo. addTodo добавляет непустую строку, deleteTodo удаляет элемент по индексу.
Конфигурация Jest
Создайте в корне проекта файл jest.config.js и добавьте конфигурацию для Next.js:
const nextJest = require("next/jest");
const createJestConfig = nextJest({
dir: "./",
});
const customJestConfig = {
moduleDirectories: ["node_modules", "/"],
testEnvironment: "jest-environment-jsdom",
};
module.exports = createJestConfig(customJestConfig); Откройте package.json и добавьте скрипт test, чтобы запускать Jest в режиме наблюдения:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest --watchAll"
},Теперь можно писать и запускать тесты.
Пишем тесты для To‑Do
Создайте папку tests в корне проекта и файл index.test.js внутри неё.
Импорты и базовая настройка:
import Home from "../src/pages/index";
import "@testing-library/jest-dom";
import { fireEvent, render, screen, waitFor, act } from "@testing-library/react";Тест на рендеринг элементов:
describe("Todo App", () => {
it("renders the todo app", () => {
render( );
expect(screen.getByTestId("todo-input")).toBeInTheDocument();
expect(screen.getByTestId("add-todo")).toBeInTheDocument();
});
});Запуск тестов:
npm run testЕсли всё настроено правильно, вы увидите, что тесты проходят.
Тестирование действий: добавление и удаление задач
Тест на добавление задачи. Мы симулируем ввод текста и клик по кнопке «Add Todo».
it("adds a todo", async () => {
render( );
const todoInput = screen.getByTestId("todo-input");
const addTodoButton = screen.getByTestId("add-todo");
const todoList = screen.getByTestId("todo-list");
await act(async () => {
fireEvent.change(todoInput, { target: { value: "New Todo" } });
addTodoButton.click();
});
await waitFor(() => {
expect(todoList).toHaveTextContent("New Todo");
});
});Чтобы смоделировать ошибку, можно изменить data-testid кнопки и запустить тест — он упадёт, сигнализируя о несоответствии тестовой инфраструктуры.
Тест на удаление задачи:
it("deletes a todo", async () => {
render( );
const todoInput = screen.getByTestId("todo-input");
const addTodoButton = screen.getByTestId("add-todo");
fireEvent.change(todoInput, { target: { value: "Todo 1" } });
fireEvent.click(addTodoButton);
const deleteTodoButton = screen.getByTestId("delete-todo-0");
fireEvent.click(deleteTodoButton);
const todoList = screen.getByTestId("todo-list");
await waitFor(() => {
expect(todoList).toBeEmptyDOMElement();
});
});Советы по стабильности тестов
Важно следовать нескольким простым правилам для надёжных тестов:
- Тестируйте поведение, а не реализацию. Ориентируйтесь на видимый интерфейс.
- Избегайте жёстких зависимостей от внутренних хелперов компонента.
- Используйте data-testid только там, где другие селекторы ненадёжны.
- Старайтесь, чтобы тесты были быстрыми и детерминированными.
Когда тесты не помогут
- Тесты не обнаружат проблем с инфраструктурой (сеть, база данных) без соответствующей интеграции или моков.
- Они не заменят E2E‑тесты для проверки реальных пользовательских сценариев.
- Неправильно написанные тесты могут дать ложное чувство безопасности.
Альтернативные подходы
- Cypress / Playwright — для E2E‑тестов и тестирования пользовательских сценариев.
- Vitest — быстрый тестовый раннер для проектов с Vite.
- React Testing Library + Jest — лучше всего подходит для unit/integration в React/Next.js.
Модель мышления при проектировании тестов
- Сценарии пользования (user stories) → 2. Критерии приёмки → 3. Юнит/интеграционные тесты для бизнес‑логики → 4. E2E‑тесты для ключевых путей.
Чек-листы для ролей
Разработчик:
- написать тесты на новую логику;
- обеспечить тестируемость (чёткость data-testid или семантические селекторы);
- запускать локально перед PR.
Код‑ревьювер:
- проверить покрытие ключевых сценариев;
- убедиться, что тесты проверяют поведение, а не реализацию;
- проследить за стабильностью тестов.
CI/DevOps:
- запускать тесты на merge/PR;
- настроить отчёты и артефакты тестирования;
- отделять быстрые unit от долгих интеграционных тестов.
Критерии приёмки
- Основной поток работает: добавление и удаление задач;
- UI‑элементы присутствуют и доступны для взаимодействия;
- Тесты запускаются в CI и проходят стабильно;
- В pull request присутствуют тесты на изменённую логику.
Шпаргалка команд и Snippet
Команды:
- npm run dev — локальная разработка
- npm run build — сборка
- npm run start — запуск продакшен‑сборки
- npm run test — запуск jest в watch‑режиме
Полезный snippet для Jest + RTL:
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// render
render( );
// взаимодействие
await userEvent.type(screen.getByRole('textbox'), 'text');
userEvent.click(screen.getByRole('button', { name: /add/i }));
// ожидание
await waitFor(() => expect(screen.getByText('text')).toBeInTheDocument());Decision tree для выбора типа теста
flowchart TD
A[Новая функциональность?] --> B{Покрытие UI или логики}
B --> |UI| C[Написать юнит/интеграционные тесты с RTL]
B --> |Бизнес-логика| D[Юнит-тесты для функций/хуков]
C --> E{Требуется симуляция реального сценария}
E --> |Да| F[Добавить E2E 'Cypress/Playwright']
E --> |Нет| G[Оставаться на unit/integration]
D --> G
F --> H[Запуск в CI и локально]Краткий глоссарий
- Юнит‑тест: проверяет одну функцию или компонент в изоляции.
- Интеграционный тест: проверяет взаимодействие нескольких компонентов или модулей.
- E2E‑тест: проверяет полный пользовательский путь в приложении.
Резюме
Это руководство показало, как добавить Jest и React Testing Library в Next.js‑проект, создать простой To‑Do‑компонент и написать тесты на рендеринг, добавление и удаление задач. Тесты повышают стабильность кода и ускоряют безопасные изменения. Расширяйте набор тестов по мере роста приложения: добавляйте интеграционные и E2E‑тесты для критичных сценариев.
Важно: автоматизация тестов в CI и дисциплина команды — ключевые факторы успеха.
Дополнительные ресурсы
- Документация Jest: https://jestjs.io
- React Testing Library: https://testing-library.com
- Next.js + testing: официальная документация Next.js
Похожие материалы
Как включить TPM 2.0 в BIOS — ASUS, Dell, HP, Lenovo
Папка профиля Firefox: где найти и как восстановить
Исправить: USB-устройство не распознано — контроллер Xbox One
Сборка Ender 3 V2 — пошаговая инструкция
Добавить Google Alerts в RSS-ленту