Тестирование Next.js приложения с помощью Jest

Почему тесты важны
Основная цель разработки — доставить в продакшн работающее приложение. Тесты помогают сохранить стабильность при добавлении функционала, рефакторинге и обновлениях зависимостей. Даже простые модульные тесты уменьшают количество багов, которые доходят до пользователей, и ускоряют локальную отладку.
Краткое определение: юнит‑тест — автоматизированная проверка логики одной единицы кода (функции или компонента).
Важно: тесты — не замена e2e и мониторинга; это комбинация уровней (unit, integration, e2e), которая даёт наилучшую защиту.
Обзор инструментов
- Jest — тестовый раннер и фреймворк утверждений. Поддерживает мокинг и снапшоты.
- @testing-library/react — фокусируется на тестировании поведения компонентов как их использует пользователь.
- jest-environment-jsdom — окружение для тестов, эмулирующее браузер.
Если вы ещё не выбрали инструменты: Jest + Testing Library — хороший баланс простоты и надёжности для React/Next.js проектов.
Создание простого To-Do приложения в Next.js
Ниже — минимальные шаги для запуска проекта и установки зависимостей (сохраните команды как есть в терминале):
- Создать проект:
npx create-next-app@latest test-tutorial- Перейти в директорию проекта:
cd nextjs-test-tutorial- Установить devDependencies:
npm install npm install --save-dev jest @testing-library/react @testing-library/jest-dom jest-environment-jsdomПосле этого можно реализовать компонент и написать тесты.
Компонент To-Do (src/pages/index.js)
Откройте файл src/pages/index.js, удалите boilerplate и добавьте следующий код (нижеследующий блок — сам компонент):
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);
};Этот код использует хук useState для хранения списка задач и поля ввода. Функция addTodo добавляет новую задачу, а deleteTodo удаляет по индексу.
JSX разметка компонента:
return (
setNewTodo(e.target.value)}
/>
{todos.map((todo, index) => (
-
{todo}
))}
{todos.map((todo, index) => (
))}
);}Совет по локализации UI: если приложение локализуется, вынесите тексты кнопок и плейсхолдеров в отдельный файл переводов.
Настройка Jest
Создайте в корне файл jest.config.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 добавьте скрипт тестов:
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "jest --watchAll"
},Запуск тестов в режиме watch удобен для локальной разработки. Для CI используйте просто jest –ci или npm test с корректными флагами.
Примеры тестов (папка tests)
Создайте папку 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();
});
});Тест добавления задачи:
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");
});
});Тест удаления задачи:
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();
});
});Запустите тесты:
npm run test


Что тестировать в компонентах
- Рендеринг ключевых элементов (инпут, кнопки, список).
- Поведение при вводе (onChange) и сабмите (click).
- Побочные эффекты — вызовы API, сохранение в localStorage (мокируются).
- Пограничные случаи: пустой ввод, длинные строки, дубли.
Важно проверять не только наличие DOM-элементов, но и поведение, которое важно пользователю.
Частые ошибки и как их избежать
- Неправильные testid: убедитесь, что testid совпадает в компоненте и тесте.
- Тесты зависят от внешнего состояния (API, время): используйте мокинг и фикстуры.
- Утечки таймеров и ассинхронных операций: очищайте моки после тестов и используйте async/await + waitFor.
Стратегии тестирования и когда применять
- Unit tests: для чистой логики и небольших компонентов — быстрые и стабильные.
- Integration tests: когда важна связка между компонентами или между компонентом и контекстом/редюсером.
- E2E tests: для проверки реального поведения приложения в браузере (Cypress, Playwright).
Ментальная модель: “тесты как фильтр“ — быстрые unit‑тесты ловят мелкие баги, integration — архитектурные ошибки, e2e — реальный поток пользователя.
Шпаргалка по Jest и Testing Library
- Локально: npm run test (watch mode)
- CI: jest –ci –reporters=default
- Запретить отдельные блоки: it.only / describe.only при отладке (не пушьте в CI)
- Очищать моки: afterEach(() => { jest.clearAllMocks(); });
Чек‑лист для ролей
Разработчик:
- Добавил тесты на новые компоненты.
- Проверил, что tests не зависят от сети.
- Запустил npm run test в режиме watch.
Технический лидер:
- Проверил покрытие ключевой бизнес‑логики.
- Подтвердил правила о тестid и соглашения по мокингу.
QA инженер:
- Сопроводил unit-тесты интеграционными или e2e для критичных сценариев.
- Настроил тесты в CI и прогнал ночные прогоны при необходимости.
Готовый план (Playbook) внедрения тестов в проект
- Выделите критичные компоненты (логика оплаты, аутентификация, формы).
- Напишите unit‑тесты для бизнес‑логики и компонентов, влияющих на безопасность/данные.
- Добавьте integration тесты для связок (API + компонент).
- Добавьте базовые e2e сценарии для основных пользовательских потоков.
- Настройте CI для прогонов тестов и отправки артефактов при падениях.
- Введите правило: коммит невозможен без прохождения локальных тестов (pre-push hook).
Критерии приёмки
- Все новые фичи покрыты тестами на 70% минимум по логике (процент покрытием может варьироваться в зависимости от проекта).
- Тесты стабильны и не дают флапов (нестабильных прохождений).
- CI прерывает релиз при падении тестов на критичных ветках.
Примеры сценариев тестирования и критерии приёмки
- Рендеринг компонента:
- Действие: отрисовать Home.
- Критерий: input и кнопка Add Todo присутствуют.
- Добавление задачи:
- Действие: ввести текст и нажать Add.
- Критерий: задача появляется в списке.
- Удаление задачи:
- Действие: добавить задачу, нажать Delete.
- Критерий: список пуст.
- Пустой ввод:
- Действие: нажать Add с пустым инпутом.
- Критерий: задача не добавляется.
Обработка ошибок и симуляция падений
Для симуляции ошибок используйте изменённые селекторы или моки, как в примере, где тест падает из‑за изменения data-testid. Это полезно, чтобы убедиться, что тесты действительно проверяют важные места. Но в проде предпочтительнее мокать внешние вызовы, а не ломать селекторы.

Important: в командной строке и CI держите отдельные конфигурации для jest (например, jest.ci.config.js), если вам нужны разные покрытия или репортеры.
Альтернативные подходы и инструменты
- Vitest — быстрый тестовый раннер, совместим с Jest API в большинстве случаев.
- Cypress/Playwright — для e2e тестов с реальным браузером.
- React Testing Library — остаётся рекомендованным для проверки поведения компонентов.
Когда валится Jest: в очень больших монорепозиториях с сотнями тестов может быть полезна миграция на Vitest ради скорости, но миграция требует проверки совместимости мокинга.
Ментальные эвристики для написания тестов
- Тестируй поведение, а не реализацию.
- Малые, независимые тесты лучше крупных интеграционных.
- Если тест часто ломается — он либо нестабилен, либо проверяет ненадёжный контракт.
Диаграмма выбора типа теста
flowchart TD
A[Новый функционал] --> B{Влияет ли на критичную бизнес-логику?}
B -- Да --> C[Unit + Integration + E2E]
B -- Нет --> D{Чистая презентация или UI?}
D -- Да --> E[Unit + Snapshot]
D -- Нет --> F[Unit тесты]
C --> G[Добавить тесты в CI]
E --> G
F --> GКороткая методология проверки (мини‑метод)
- Определить «что важно» в фиче (ключевые действия пользователя).
- Написать тесты, которые проверяют именно это поведение.
- Мокать внешние зависимости, чтобы тесты были быстрыми.
- Запустить локально, затем в CI.
Сниппеты и команды (cheat sheet)
- Установить зависимости: см. раздел Создание проекта.
- Запустить тесты в интерактивном режиме: npm run test
- Запустить один тестовый файл: npx jest path/to/file.test.js
- Очистить моки: jest.clearAllMocks() в afterEach
Мини‑глоссарий
- Юнит: единица кода (функция, компонент).
- Мок: заменитель внешней зависимости в тесте.
- Снапшот: сохранённое представление компонента для сравнения изменений.
Краткое резюме
Тестирование — обязательная часть разработки продакшн‑готовых приложений. Начните с юнит‑тестов на критичных компонентах, добавьте интеграционные и e2e там, где это оправдано. Jest + Testing Library дают простой и мощный набор для React/Next.js проектов.
Если вам нужно, я могу подготовить готовый набор тестов для вашего репозитория или написать конфигурацию CI для автоматического прогона тестов.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone