Анатомия теста

Jest — это популярный фреймворк для тестирования JavaScript, предоставляющий мощные средства для написания и выполнения тестов. Один из ключевых элементов работы с Jest — это структура теста. Понимание анатомии теста позволяет эффективно писать, поддерживать и масштабировать тесты в проекте.

Структура теста

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

1. Описание теста

Каждый тест начинается с функции test() или it(). Это два синонима, и выбор между ними зависит от предпочтений разработчика, но оба они выполняют одну и ту же функцию — задают описание и тело теста.

Пример:

test('должен правильно сложить два числа', () => {
  // тестовая логика
});

Описание теста должно чётко и кратко отражать, что именно проверяется в тестируемом фрагменте кода. Хорошее описание помогает легко понять, что именно делает тест, даже не вникая в его логику.

2. Тело теста

Тело теста — это функция, которая непосредственно описывает, что тестируемый код должен делать. Обычно здесь происходит вызов функции, которую мы тестируем, и проверка её поведения. Важно, чтобы тест был самодостаточен и выполнялся быстро.

Пример:

test('должен правильно сложить два числа', () => {
  const result = sum(2, 3);
  expect(result).toBe(5);
});

Здесь вызов функции sum() выполняет нужное действие, а проверка ожидаемого результата происходит с помощью expect().

3. Ожидания (Assertions)

Ожидания — это утверждения, которые проверяют, соответствует ли результат выполнения тестируемого кода ожидаемому результату. Jest предоставляет несколько типов утверждений, которые позволяют проверить, является ли результат теста правильным.

Примеры:

  • toBe() — проверка на строгую эквивалентность.

    expect(result).toBe(5);
  • toEqual() — проверка глубокого сравнения объектов и массивов.

    expect(result).toEqual({ a: 1, b: 2 });
  • toBeTruthy() / toBeFalsy() — проверка на истинность или ложность значения.

    expect(result).toBeTruthy();
    expect(result).toBeFalsy();
  • toHaveBeenCalled() — проверка, что функция была вызвана.

    expect(mockFunction).toHaveBeenCalled();

Ожидания позволяют точнее определить, как именно должен вести себя код в различных ситуациях.

Дополнительные компоненты теста

Помимо базовых компонентов теста (описание, тело теста и ожидания), Jest предоставляет и другие средства для более сложных сценариев тестирования.

Мокирование функций и данных

Для того чтобы изолировать тестируемую функцию от зависимостей, часто приходится использовать моки. Jest предоставляет мощные инструменты для создания моков — функции, объектов или модулей, которые имитируют поведение реальных зависимостей.

Пример создания мока:

const mockFn = jest.fn();
mockFn.mockReturnValue(42);

test('функция возвращает 42', () => {
  expect(mockFn()).toBe(42);
});

Таймеры и асинхронность

Многие тесты требуют работы с асинхронными функциями, таймерами или задержками. Jest имеет встроенную поддержку для работы с асинхронным кодом. Например, можно использовать async/await, а также методы для работы с таймерами, такие как jest.useFakeTimers().

Пример асинхронного теста:

test('должен вернуть результат после задержки', async () => {
  const result = await asyncFunction();
  expect(result).toBe('some result');
});

Очистка после тестов

Для обеспечения чистоты тестов и предотвращения побочных эффектов, часто необходимо очищать данные или сбрасывать состояние между тестами. Jest предоставляет специальные хуки для этого: beforeAll(), afterAll(), beforeEach(), afterEach().

Пример:

let data;

beforeEach(() => {
  data = initializeData();
});

test('тест с инициализацией данных', () => {
  expect(data).toBeDefined();
});

Группировка тестов

Для того чтобы лучше организовать тесты, Jest позволяет группировать их с помощью функций describe(). Это позволяет создать структуру тестов, сгруппированных по функциональности, что улучшает читаемость и поддерживаемость тестов.

Пример:

describe('проверка математических операций', () => {
  test('должен складывать два числа', () => {
    expect(sum(1, 2)).toBe(3);
  });

  test('должен вычитать два числа', () => {
    expect(subtract(5, 2)).toBe(3);
  });
});

Группировка тестов позволяет наглядно разделять тесты на логические блоки и упростить поиск ошибок.

Параллельное выполнение тестов

Jest выполняет тесты параллельно, что ускоряет процесс их выполнения. Однако, иногда возникает необходимость в синхронизации тестов, если они зависят от какого-то общего состояния. В таких случаях можно использовать хуки или механизмы изоляции, такие как моки или фейковые таймеры.

Данные и параметры в тестах

В некоторых случаях тесты требуют проверки нескольких вариантов данных или параметров. Для этого можно использовать такие инструменты как test.each() или describe.each(), которые позволяют запускать один и тот же тест с разными входными данными.

Пример:

describe.each([
  [1, 2, 3],
  [2, 3, 5],
  [3, 4, 7]
])('сложение %i и %i', (a, b, sum) => {
  test(`должно давать ${sum}`, () => {
    expect(a + b).toBe(sum);
  });
});

Этот способ позволяет сократить количество повторяющегося кода, улучшая его читаемость и поддерживаемость.

Механизм отчётности и вывода результатов

После выполнения тестов Jest предоставляет подробные отчёты о результатах, в которых можно увидеть, какие тесты прошли, а какие не прошли. Отчёты могут быть настроены для более детализированного анализа, например, с выводом информации о времени выполнения тестов, ошибках и выполненных мока-сценариях.

Заключение

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