Создание пользовательских матчеров

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

Что такое матчеры?

Матчеры — это функции, которые сравнивают значения в тестах с ожидаемыми результатами. Jest поставляется с набором стандартных матчеров, таких как .toBe(), .toEqual(), .toBeTruthy() и другие. Однако стандартных методов может не хватить для некоторых сложных случаев, например, когда нужно проверить структуру объекта, асинхронные операции или более специфические условия.

Зачем нужны пользовательские матчеры?

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

  • Улучшить читаемость тестов, делая их более декларативными.
  • Снизить повторяемость кода, так как часто используемые проверки можно вынести в отдельные функции.
  • Расширить функциональность Jest, добавив проверку нестандартных условий.

Создание простого пользовательского матчера

Для начала рассмотрим создание самого простого пользовательского матчера. В Jest пользовательский мачтер создаётся с помощью функции expect.extend().

Пример создания простого матчера для проверки, является ли строка палиндромом:

expect.extend({
  toBePalindrome(received) {
    const reversed = received.split('').reverse().join('');
    const pass = received === reversed;
    if (pass) {
      return {
        message: () => `expected ${received} not to be a palindrome`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected ${received} to be a palindrome`,
        pass: false,
      };
    }
  },
});

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

  • toBePalindrome — это имя кастомного матчера.

  • Внутри матчера проверяется, является ли строка палиндромом, то есть одинаково ли она читается с обеих сторон.

  • received — это значение, которое передаётся в мачтер, и с которым будет производиться сравнение.

  • Функция возвращает объект с двумя полями:

    • message: строка с сообщением, которая будет выводиться в случае неудачи.
    • pass: булевое значение, указывающее, прошёл ли тест или нет.

Использование пользовательского матчера

После создания пользовательского матчера его можно использовать так же, как и стандартные матчеры Jest:

test('проверка палиндрома', () => {
  expect('madam').toBePalindrome();
  expect('hello').not.toBePalindrome();
});

Расширенные возможности пользовательских матчеров

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

Асинхронные матчеры

Jest поддерживает асинхронные тесты, и пользовательские матчеры также могут быть асинхронными. Для этого нужно вернуть из функции мачтера Promise. Рассмотрим пример:

expect.extend({
  async toBeResolved(received) {
    try {
      await received;
      return {
        message: () => `expected promise to be rejected`,
        pass: true,
      };
    } catch (error) {
      return {
        message: () => `expected promise to be resolved`,
        pass: false,
      };
    }
  },
});

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

Применение такого матчера:

test('проверка асинхронного значения', async () => {
  await expect(Promise.resolve('some value')).toBeResolved();
  await expect(Promise.reject(new Error('some error'))).not.toBeResolved();
});

Мачтеры для сложных объектов

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

expect.extend({
  toHaveRequiredFields(received, requiredFields) {
    const missingFields = requiredFields.filter(field => !(field in received));
    const pass = missingFields.length === 0;

    if (pass) {
      return {
        message: () => `expected object not to have fields: ${missingFields.join(', ')}`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected object to have fields: ${missingFields.join(', ')}`,
        pass: false,
      };
    }
  },
});

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

test('проверка объекта на обязательные поля', () => {
  const user = { name: 'John', age: 30 };
  expect(user).toHaveRequiredFields(['name', 'age']);
  expect(user).not.toHaveRequiredFields(['name', 'email']);
});

Рекомендации по созданию пользовательских матчеров

  1. Консистентность: Создавая пользовательские матчеры, старайтесь придерживаться стандартного стиля, который используется в Jest. Например, используйте метод toBe для строгих сравнений, а toEqual — для глубоких проверок.

  2. Читаемость: Постарайтесь, чтобы ваши пользовательские матчеры были легко читаемы и интуитивно понятны. Название мачтера должно отражать его поведение (например, toHaveRequiredFields, toBePalindrome).

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

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

Заключение

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