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

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

Основы работы с test.each

Функция test.each принимает два основных параметра: массив данных и сам тест, который будет выполнен для каждого элемента из этого массива. Каждый элемент массива передаётся в качестве аргумента в тестовую функцию, что позволяет легко повторно запускать один и тот же тест с различными входными данными.

Пример базового использования:

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

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

Формат данных для test.each

Данные для test.each могут быть представлены в различных форматах:

  • Массив массивов: Каждый подмассив содержит параметры для теста.

    test.each([
      [a, b, result],
      [a2, b2, result2],
      ...
    ])('%i + %i = %i', (a, b, result) => {
      expect(a + b).toBe(result);
    });
  • Массив объектов: Если входные данные сложнее, можно использовать массив объектов с именованными полями.

    test.each([
      { a: 1, b: 2, expected: 3 },
      { a: 2, b: 3, expected: 5 },
      { a: 3, b: 4, expected: 7 }
    ])('сложение %i и %i даёт %i', ({ a, b, expected }) => {
      expect(a + b).toBe(expected);
    });
  • Шаблон строк: Для использования строк в качестве шаблонов, можно указать параметры через шаблонные строки.

    test.each([
      [1, 2, 3],
      [2, 3, 5]
    ])('сложение %i + %i должно быть равно %i', (a, b, expected) => {
      expect(a + b).toBe(expected);
    });

Поддержка асинхронных тестов

Если тесты асинхронные, то с помощью async/await можно легко реализовать асинхронную проверку:

test.each([
  [1, 2, 3],
  [2, 3, 5]
])('сложение %i и %i даёт %i', async (a, b, expected) => {
  const result = await someAsyncFunction(a, b);
  expect(result).toBe(expected);
});

Является ли test.each полезным для сложных тестов?

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

Использование с динамическими значениями

Можно также передавать динамические значения в test.each, например, результат выполнения функции. В таких случаях стоит быть осторожным, так как вызов функции будет происходить каждый раз перед запуском теста:

const generateTestCases = () => [
  [1, 2, 3],
  [2, 3, 5],
  [3, 4, 7]
];

test.each(generateTestCases())('сложение %i и %i даёт %i', (a, b, expected) => {
  expect(a + b).toBe(expected);
});

Здесь generateTestCases возвращает массив, который используется в test.each. Это позволяет гибко подходить к созданию тестов с переменными данными.

Советы по использованию

  • Читаемость тестов: Использование test.each делает тесты компактными, но важно следить за их читаемостью. Выводимые строки, содержащие параметризированные данные, должны быть ясными и понятными.

  • Уникальные имена тестов: Шаблоны строк в test.each позволяют создавать уникальные имена тестов, что упрощает их идентификацию в отчёте о тестах.

    Пример:

    test.each([
      [1, 2, 3],
      [2, 3, 5]
    ])('при сложении %i и %i результат должен быть %i', (a, b, expected) => {
      expect(a + b).toBe(expected);
    });
  • Тесты с большим количеством данных: Если наборы данных очень большие, можно использовать test.each с сокращениями или слайсами массивов, чтобы не перегружать тесты.

Отладка и ошибки

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

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

Совмещение с другими функциями Jest

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

Пример:

let mockFunction;

beforeEach(() => {
  mockFunction = jest.fn();
});

test.each([
  [1, 2, 3],
  [2, 3, 5]
])('сложение %i и %i даёт %i', (a, b, expected) => {
  mockFunction(a, b);
  expect(mockFunction).toHaveBeenCalledWith(a, b);
  expect(a + b).toBe(expected);
});

Здесь мы подготавливаем mockFunction с помощью beforeEach и проверяем, что она вызывается с ожидаемыми параметрами.

Заключение

Использование test.each значительно упрощает написание тестов, которые требуют повторного использования одной и той же логики с разными данными. Это мощный инструмент для параметризированного тестирования, который позволяет повысить читаемость и поддерживаемость тестов, а также минимизировать дублирование кода.