Гид по технологиям

Jest для юнит‑тестирования JavaScript и React

7 min read Тестирование Обновлено 14 Dec 2025
Jest: юнит‑тесты для JavaScript и React
Jest: юнит‑тесты для JavaScript и React

Логотип фреймворка Jest

Быстрые ссылки

  • Что такое юнит‑тестирование?
  • Начало работы с Jest
  • Тестирование React

Что такое юнит‑тестирование?

Тестирование — это автоматическая проверка небольших частей кода (единиц), чтобы убедиться, что они работают независимо и не ломаются при дальнейшем развитии проекта. “Юнит” может означать функцию, класс или компонент — по сути, наименьшую часть, которую удобно проверять отдельно.

Зачем это делать:

  • Обнаруживать регрессии при изменениях в коде.
  • Документировать ожидаемое поведение функции или компонента.
  • Облегчать работу с наследуемым (legacy) кодом.

Краткая формула: написали функцию — пишете тест, который передаёт входные данные и проверяет ожидаемый результат.

Важно: юнит‑тесты проверяют поведение единиц кода, а не обязательно интеграцию с внешними системами — для этого нужны интеграционные и e2e‑тесты.

Основная идея работы с Jest

Jest запускает набор тестов, которые вы описываете в файлах с тестами. Каждый тест использует утверждения (matchers), такие как toBe, toEqual, toThrow и т.д., чтобы сравнить фактический результат с ожидаемым.

Короткие определения терминов:

  • Юнит: наименьшая единица тестирования (функция, компонент, класс).
  • Matcher: функция для проверки утверждений (toBe, toEqual и т. п.).
  • Мок (mock): замена зависимости тестируемой единицы для контроля поведения.

Начало работы с Jest (локальный проект)

Чтобы быстро настроить проект с Jest:

  1. Инициализируйте npm‑проект:
npm init -y
  1. Установите Jest как dev‑зависимость:
npm install --save-dev jest
  1. Добавьте скрипт запуска тестов в package.json:
"scripts": {
  "test": "jest"
}
  1. Создайте простую функцию для тестирования, например src/doSomeMath.js:
function doSomeMath(a, b) {
  return a + b;
}

module.exports = doSomeMath;
  1. Создайте файл тестов tests/doSomeMath.test.js:
const doSomeMath = require('../src/doSomeMath');

test('adds 2 + 2 to equal 4', () => {
  expect(doSomeMath(1, 1)).toBe(2);
  expect(doSomeMath(2, 2)).toBe(4);
});
  1. Запустите тесты:
npm run test

Jest автоматически находит файлы в каталогах __tests__, test и файлы с суффиксом .test.js или .spec.js.

Обзор matchers и полезных приёмов

Некоторые часто используемые matchers:

  • .not.* — инвертирует matcher.
  • toBe() — строгая равенство (Object.is).
  • toEqual() — глубокое сравнение объектов.
  • toStrictEqual() — как toEqual, но с проверкой отсутствия неявных undefined полей.
  • toBeTruthy() / toBeFalsy() — логические проверки.
  • toBeNull() / toBeUndefined() / toBeDefined() — разные состояния значения.
  • toContain() — проверка наличия элемента в массиве или подстроки в строке.
  • toMatch() — проверка с регулярным выражением.
  • toThrow() — проверка, что функция бросает исключение.

Совет: в одном тесте можно использовать несколько expect() чтобы проверять разные сценарии, но лучше делить тесты по поведению для читаемости.

Пример параметризованного теста

Если нужно проверить функцию на многих входных данных, можно использовать цикл или встроенные возможности Jest, например test.each:

test.each([
  [1, 5, 6],
  [2, 5, 7],
  [3, 5, 8]
])('doSomeMath(%i, %i) === %i', (a, b, expected) => {
  expect(doSomeMath(a, b)).toBe(expected);
});

Простой цикл тоже подойдёт, но test.each даёт лучшую читаемость и отдельные результирующие строки в отчёте.

Пример структуры AAA (Arrange, Act, Assert)

Хорошая практическая модель для теста — Arrange, Act, Assert:

  • Arrange: подготовить данные и окружение.
  • Act: вызвать тестируемую функцию.
  • Assert: проверить результат.

Пример:

// Arrange
const a = 2;
const b = 3;

// Act
const result = doSomeMath(a, b);

// Assert
expect(result).toBe(5);

Тестирование React‑компонентов с Jest и React Testing Library

Create React App устанавливает Jest и React Testing Library по умолчанию.

  1. Создайте приложение:
npx create-react-app jest-react
  1. Измените src/App.js на следующий компонент:
import React, {useState} from 'react';
import './App.css';

function App() {
  const [disabled, setDisabled] = useState(false);

  function handleClick() {
    setDisabled(!disabled);
  }

  return (
    
  );
}

export default App;
  1. Тест src/App.test.js:
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from './App';

test('disabled button on click', () => {
  const button = render().getByTestId('useless-button');
  expect(button.disabled).toBeFalsy();
  fireEvent.click(button, {button: 1});
  expect(button.disabled).toBeTruthy();
});

Пояснение: тест рендерит компонент, находит кнопку по data-testid, проверяет начальное состояние, симулирует клик и проверяет обновлённый HTML.

Отчёт тестов Jest с зелёными прогонами

Расширенные приёмы: мокирование и шпионы

Jest предоставляет встроенные возможности для создания моков (jest.fn), подмены модулей (jest.mock) и шпионажа за функциями (jest.spyOn).

Пример мок‑функции:

const myMock = jest.fn().mockReturnValue(42);
expect(myMock()).toBe(42);
expect(myMock).toHaveBeenCalled();

Мок модуля:

// Вместо реального модуля
jest.mock('../src/api', () => ({
  fetchData: jest.fn().mockResolvedValue({data: 'ok'})
}));

Шпион (spyOn) позволяет перехватить вызовы существующей функции без полного мока:

const spy = jest.spyOn(Math, 'random').mockReturnValue(0.5);
// ...тесты...
spy.mockRestore();

Важно восстанавливать исходное поведение после теста через mockRestore() или в afterEach.

Асинхронное тестирование

Для промисов и async/await используйте async тесты и await для ожидания результатов:

test('fetches data', async () => {
  const data = await fetchData();
  expect(data).toEqual({ok: true});
});

Если протестировать reject, можно использовать await expect(promise).rejects.toThrow().

Snapshot‑тесты

Снапшоты полезны для проверки неизменного вывода компонента (JSX). Пример с React Testing Library и renderer:

import renderer from 'react-test-renderer';
import App from './App';

it('renders consistently', () => {
  const tree = renderer.create().toJSON();
  expect(tree).toMatchSnapshot();
});

Снапшоты удобно обновлять при явных изменениях (jest -u), но злоупотреблять ими не стоит: они должны быть атомарными и легко обозримыми.

Конфигурация Jest

Если нужно больше контроля, создайте jest.config.js или добавьте секцию jest в package.json. Примеры опций:

  • testEnvironment: ‘jsdom’ или ‘node’
  • setupFilesAfterEnv: массив файлов для настройки тестовой среды
  • collectCoverage: true
  • coverageDirectory: ‘coverage’

Пример минимального jest.config.js:

module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['/src/setupTests.js'],
  collectCoverage: true,
  coverageDirectory: 'coverage'
};

Интеграция в CI (GitHub Actions пример)

Простейший workflow для запуска тестов на push:

name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      - run: npm ci
      - run: npm test --silent

Добавьте отчёт о покрытии и тщательно проверяйте 실패 ветки в PR.

Лучшие практики

  • Пишите тесты для критической логики и публичных API модулей.
  • Держите тесты независимыми: не полагайтесь на порядок выполнения.
  • Используйте beforeEach/afterEach для очистки глобального состояния.
  • Ограничивайте использование глобальных моков — предпочитайте локальные jest.mock в контексте теста.
  • Придерживайтесь понятных имён тестов: describe + it/test или читаемые строки. Например: test('возвращает сумму двух чисел').
  • Укажите порог покрытия (coverage) в CI, но не гонитесь за 100% любой ценой — важно качество тестов, а не только показатели.

Шаблоны имен и структура репозитория

Рекомендуемая структура:

  • src/ — код
  • tests/ или src/tests/ — файлы тестов
  • src/.test.js / .spec.js — тесты рядом с кодом облегчают рефакторинг

Пример именования теста:

  • Метод: methodName_shouldDoSomething_whenCondition — часто слишком длинно
  • Читаемый подход: test('возвращает ожидаемый результат при корректном вводе') — лучше для локального понимания

Распространённые ошибки и как их исправлять

  1. Тесты зависят от времени (вызовы Date): мокируйте дату или используйте фикстуры.
  2. Мутируемый глобальный стейт между тестами: очищайте state в afterEach.
  3. Неправильная проверка объектов: для объектов используйте toEqual, а не toBe.
  4. Проблемы с асинхронностью: забыли await — результаты теста непредсказуемы.
  5. Слишком большие снапшоты: дробите компонент на меньшие части или проверяйте конкретные свойства.

Когда юнит‑тесты не подходят

  • Когда поведение зависит от реальной внешней инфраструктуры (БД, очередь) — лучше интеграционные тесты с тестовой средой.
  • Для тестирования визуальной регрессии сложных интерфейсов могут потребоваться визуальные тесты или e2e (Cypress, Playwright).

Альтернативы и дополнения к Jest

  • Mocha + Chai + Sinon — более модульный подход, если нужен гибкий раннер и assertion‑библиотека.
  • Vitest — быстрый тестовый раннер для проектов на Vite с API, похожим на Jest.
  • Cypress / Playwright — для e2e и тестов браузера.

Выбор зависит от сборки проекта, производительности и интеграции с остальным стеком.

Чеклист ролей при внедрении тестов

Разработчик:

  • Написал тесты для нового кода
  • Моки локальны и не ломают другие тесты
  • Тесты читаемы и быстры

Ревьюер:

  • Проверил сценарии и крайние случаи
  • Убедился, что тесты не дублируют логику реализации
  • Убедился, что тесты проходят локально

Инженер CI/DevOps:

  • Настроил запуск тестов в CI
  • Собрал отчёт о покрытии
  • Добавил уведомления о падении тестов в PR

Критерии приёмки

  • Все юнит‑тесты проходят локально и в CI.
  • Критические функции покрыты тестами.
  • Отсутствуют неожиданные глобальные мок‑эффекты между тестами.
  • Быстродействие тестов приемлемо (обычно единицы/десятки секунд на CI для небольших репозиториев).

Краткий глоссарий (1‑строчные определения)

  • Юнит: минимальная проверяемая часть кода.
  • Мок: искусственная реализация зависимости в тесте.
  • Снапшот: сохранённый сериализованный вывод для сравнения.
  • Matcher: функция для проверки ожидаемого результата.

Чеклист «быстрое исправление» при падении тестов

  1. Прочитать сообщение об ошибке и стектрейс.
  2. Локально запустить только упавший тест (jest path/to/test -t 'имя теста').
  3. Проверить, не менялись ли внешние зависимости (API, дата, окружение).
  4. Если мок ломается — восстановить моки через mockRestore в afterEach.
  5. Для flaky tests — определить причину и исправить (асинхронность, тайминги, сетевые вызовы).

Советы по производительности

  • Используйте --runInBand локально при отладке, но в CI позволяйте Jest распараллеливать.
  • Ограничьте тяжёлые операции: не запускайте интеграционные тесты в той же группе, что и быстрые unit.
  • Кешируйте зависимости в CI (npm ci / yarn cache) для ускорения повторных запусков.

Пример полного набора тестов: unit, async, mock

const doSomeMath = require('../src/doSomeMath');
const api = require('../src/api');

jest.mock('../src/api');

beforeEach(() => {
  api.fetchData.mockClear();
});

test('sums numbers', () => {
  expect(doSomeMath(1,2)).toBe(3);
});

test('handles async fetch', async () => {
  api.fetchData.mockResolvedValue({value: 10});
  const result = await api.fetchData();
  expect(result.value).toBe(10);
});

Итог

Jest — мощный, но при этом простой инструмент для написания юнит‑тестов в JavaScript и React. Он покрывает большинство сценариев: от простых синхронных тестов до моков, снапшотов и асинхронных вызовов. Внедряйте тесты итерационно, ставьте разумные пороги покрытия, автоматизируйте запуск в CI и следите за качеством тестов так же внимательно, как и за качеством кода.

Важно: тесты — это не бюрократия, а инструмент повышения уверенности в изменениях кода.


Summary:

  • Что дальше: начните с критичных функций и компонента, добавьте тесты в CI и отлавливайте flaky‑тесты первыми.

Important:

  • Не делайте тесты слишком связанными с реализацией — проверяйте поведение, а не внутренности.

Notes:

  • Обновляйте снапшоты только осознанно и документируйте причину изменения.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

RDP: полный гид по настройке и безопасности
Инфраструктура

RDP: полный гид по настройке и безопасности

Android как клавиатура и трекпад для Windows
Гайды

Android как клавиатура и трекпад для Windows

Советы и приёмы для работы с PDF
Документы

Советы и приёмы для работы с PDF

Calibration в Lightroom Classic: как и когда использовать
Фото

Calibration в Lightroom Classic: как и когда использовать

Отключить Siri Suggestions на iPhone
iOS

Отключить Siri Suggestions на iPhone

Рисование таблиц в Microsoft Word — руководство
Office

Рисование таблиц в Microsoft Word — руководство