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

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

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

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

Использование test.each()

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

Синтаксис

test.each(table)(name, fn, timeout)
  • table — это массив, содержащий данные, которые будут переданы в тест. Каждое значение в массиве будет передано как отдельный набор данных.
  • name — строка, которая будет использована в качестве имени теста. Она может содержать параметры, которые затем будут заменены значениями из набора данных.
  • fn — сама тестовая функция, которая будет выполнена для каждого набора данных.
  • timeout — необязательный параметр, который задаёт тайм-аут выполнения теста.

Пример использования test.each()

Рассмотрим пример, где тестируется функция сложения двух чисел:

function sum(a, b) {
  return a + b;
}

test.each([
  [1, 2, 3],
  [2, 3, 5],
  [4, 5, 9],
])('сложение %i и %i равно %i', (a, b, expected) => {
  expect(sum(a, b)).toBe(expected);
});

В данном примере:

  • Массив данных [1, 2, 3], [2, 3, 5], [4, 5, 9] передаётся в метод test.each().
  • Каждый набор данных представляет собой массив, где первые два значения — это входные параметры для функции sum(), а третье — ожидаемый результат.
  • В строке сложение %i и %i равно %i используются параметры для подстановки значений.

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

Параметризация с использованием объектов

Для более сложных данных можно использовать объекты вместо массивов. Это полезно, если тестируемая функция принимает именованные параметры или если нужно передать больше данных для каждого теста.

Пример с объектами

function greet(person) {
  return `Привет, ${person.name}!`;
}

test.each([
  [{ name: 'Иван' }, 'Привет, Иван!'],
  [{ name: 'Мария' }, 'Привет, Мария!'],
])('приветствие для %o', (person, expected) => {
  expect(greet(person)).toBe(expected);
});

В этом примере:

  • Каждый элемент массива данных является объектом.
  • Метод greet принимает объект, и в тесте проверяется корректность генерации строки с приветствием.

Множественные параметры

Если необходимо передать более двух параметров в тест, это можно легко реализовать с помощью test.each(), передав в таблицу больше значений. Метод test.each() поддерживает любое количество параметров, что делает его гибким инструментом для тестирования различных функций.

Пример с несколькими параметрами

function multiply(a, b, c) {
  return a * b * c;
}

test.each([
  [2, 3, 4, 24],
  [1, 5, 6, 30],
  [0, 7, 8, 0],
])('умножение %i, %i и %i дает %i', (a, b, c, expected) => {
  expect(multiply(a, b, c)).toBe(expected);
});

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

Параметризация с асинхронными функциями

Jest также поддерживает асинхронные функции в параметризованных тестах. Для этого необходимо вернуть промис или использовать async/await в тестовой функции.

Пример с асинхронной функцией

async function fetchData(url) {
  const response = await fetch(url);
  return response.json();
}

test.each([
  ['https://api.example.com/user1', { id: 1, name: 'Иван' }],
  ['https://api.example.com/user2', { id: 2, name: 'Мария' }],
])('получение данных с %s', async (url, expected) => {
  const data = await fetchData(url);
  expect(data).toEqual(expected);
});

В этом примере для каждого URL вызывается асинхронная функция fetchData, и проверяется, что полученные данные соответствуют ожидаемым. Параметризованные тесты работают так же, как и с синхронными функциями, только с добавлением асинхронности.

Динамическое создание тестов

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

Пример динамических данных

const testCases = [
  { input: [1, 2], expected: 3 },
  { input: [2, 3], expected: 5 },
  { input: [4, 5], expected: 9 },
];

testCases.forEach(({ input, expected }) => {
  test(`сложение ${input[0]} и ${input[1]} равно ${expected}`, () => {
    expect(sum(...input)).toBe(expected);
  });
});

Здесь создаются тесты динамически на основе массива объектов testCases, и для каждого объекта запускается тест с соответствующими параметрами. Такой подход полезен, если данные могут изменяться во время выполнения тестов.

Рекомендации

  1. Читаемость: Несмотря на то, что параметризация тестов сокращает количество строк кода, важно, чтобы тесты оставались читаемыми. Строки с описанием теста должны чётко отражать, что именно проверяется.
  2. Группировка тестов: Если необходимо протестировать один и тот же код в разных ситуациях, параметризованные тесты — это хорошее решение. Однако, если количество тестов слишком велико, стоит использовать дополнительные средства группировки, такие как describe.
  3. Покрытие всех кейсов: Важно убедиться, что для каждого набора данных охвачены все возможные сценарии работы тестируемой функции. Параметризованные тесты хорошо подходят для проверки функции с различными входными данными, но стоит также проверять крайние случаи (например, пустые или нулевые значения).

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