Jest основы

Jest — фреймворк для тестирования JavaScript и TypeScript с встроенными assertion-матчерами, mock-системой и генератором coverage-отчётов.

Зачем нужно

Jest — стандарт де-факто в экосистеме JavaScript: он работает «из коробки» без сложной конфигурации, поддерживает CJS и ESM, умеет параллельно запускать тесты и измерять покрытие кода. Его mock-система позволяет полностью контролировать зависимости, а watch-режим ускоряет цикл разработки.

Где используется

  • Unit-тесты функций, классов и сервисов в Node.js и браузерных проектах
  • Integration-тесты API с mock-зависимостями
  • Тестирование React-компонентов совместно с Testing Library
  • В CI/CD: jest --ci для детерминированного запуска без интерактивного режима

Основной контент

Установка и настройка

npm install --save-dev jest

# Для TypeScript
npm install --save-dev jest @types/jest ts-jest
// package.json
{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage --coverageReporters=text"
  },
  "jest": {
    "testEnvironment": "node",
    "testMatch": ["**/__tests__/**/*.js", "**/*.test.js"],
    "collectCoverageFrom": ["src/**/*.js", "!src/**/*.d.ts"]
  }
}

Базовые API

// describe — группировка тестов
describe('Название группы', () => {

  // test / it — один тест (взаимозаменяемы)
  test('что именно проверяем', () => {
    expect(2 + 2).toBe(4);
  });

  it('альтернативный синтаксис', () => {
    expect([1, 2, 3]).toHaveLength(3);
  });
});

Хуки жизненного цикла

describe('UserService', () => {
  let service;

  beforeAll(() => {
    // Один раз перед всей группой (например, подключение к БД)
  });

  afterAll(() => {
    // Один раз после всей группы (закрытие соединений)
  });

  beforeEach(() => {
    service = new UserService(); // перед каждым тестом
  });

  afterEach(() => {
    jest.clearAllMocks; // после каждого теста
  });

  test('...', () => { /* ... */ });
});

Асинхронные тесты

// async/await — рекомендуемый подход
test('получает пользователя по ID', async () => {
  const user = await fetchUser(1);
  expect(user.name).toBe('Alice');
});

// Промис — возвращай промис из теста
test('отклоняет невалидный ID', () => {
  return expect(fetchUser(-1)).rejects.toThrow('Invalid ID');
});

Полезные матчеры

// Значения
expect(x).toBe(42);               // строгое ===
expect(obj).toEqual({ a: 1 });    // глубокое равенство
expect(x).toBeTruthy();
expect(x).toBeFalsy();
expect(x).toBeNull();
expect(x).toBeUndefined();
expect(x).toBeDefined();

// Числа
expect(n).toBeGreaterThan(0);
expect(n).toBeLessThanOrEqual(100);
expect(n).toBeCloseTo(3.14, 2);   // ≈ с точностью 2 знака

// Строки и массивы
expect(str).toContain('hello');
expect(arr).toContain(42);
expect(arr).toHaveLength(3);
expect(arr).toEqual(expect.arrayContaining([1, 2]));

// Ошибки
expect( => fn).toThrow('message');
expect( => fn).toThrow(TypeError);

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

npx jest                          # все тесты
npx jest math.test.js             # конкретный файл
npx jest --testNamePattern="add"  # по имени теста
npx jest --watch                  # режим наблюдения
npx jest --coverage               # с отчётом покрытия
npx jest --ci                     # для CI (без интерактива)
npx jest --verbose                # подробный вывод

Частые ошибки

  • Забыть return или await в асинхронном тесте — тест пройдёт зелёным, даже если промис отклонён
  • toBe для объектов — сравнивает ссылки, а не содержимое; используй toEqual
  • Глобальное состояние между тестами — всегда используй beforeEach для создания свежего экземпляра
  • Слишком общее имя тестаtest('works') ничего не говорит; пиши test('возвращает null для несуществующего ID')
  • Игнорировать ошибки покрытия — 100% coverage не гарантирует правильность, но ниже 70% — сигнал о проблемах

Связанные темы

Ресурсы