Создание custom matchers advanced

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

Что такое кастомные матчеры

Кастомные матчеры в Jasmine — это функции, которые позволяют создавать собственные условия проверки для объектов, значений или поведения. В отличие от стандартных Jasmine матчеров, таких как toBe, toEqual или toContain, кастомные матчеры предоставляют возможность задать уникальную логику проверки, которая не входит в базовый функционал фреймворка.

Структура кастомного матчера

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

  • compare — функция, которая выполняет проверку и возвращает результат.
  • message — необязательная строка, которая будет использована в выводах при ошибках.

Типичная структура кастомного матчера выглядит следующим образом:

beforeEach(function() {
  jasmine.addMatchers({
    toBeOdd: function() {
      return {
        compare: function(actual) {
          const result = {};
          result.pass = actual % 2 !== 0;
          result.message = `Expected ${actual} to be odd.`;
          return result;
        }
      };
    }
  });
});

В этом примере создается кастомный матч toBeOdd, который проверяет, является ли число нечётным. Функция compare принимает одно значение (в данном случае, число), выполняет проверку и возвращает объект с двумя свойствами:

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

Регистрация кастомных матчеров

Кастомные матчеры добавляются в Jasmine с помощью метода addMatchers. Это необходимо делать в блоках beforeEach, чтобы гарантировать, что они будут доступны перед выполнением тестов.

beforeEach(function() {
  jasmine.addMatchers({
    toBePrime: function() {
      return {
        compare: function(actual) {
          const result = {};
          if (actual < 2) {
            result.pass = false;
            result.message = `${actual} is not a prime number.`;
          } else {
            result.pass = true;
            for (let i = 2; i < actual; i++) {
              if (actual % i === 0) {
                result.pass = false;
                result.message = `${actual} is divisible by ${i}.`;
                break;
              }
            }
          }
          return result;
        }
      };
    }
  });
});

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

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

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

describe("Prime number tests", function() {
  it("should check if 7 is a prime number", function() {
    expect(7).toBePrime();
  });

  it("should check if 8 is a prime number", function() {
    expect(8).toBePrime();
  });
});

Когда тест будет выполняться, Jasmine использует кастомный матч toBePrime для проверки чисел 7 и 8. В случае ошибки, выводится соответствующее сообщение.

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

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

beforeEach(function() {
  const numberMatchers = ['Even', 'Odd', 'Prime'];
  numberMatchers.forEach(function(type) {
    jasmine.addMatchers({
      [`toBe${type}`]: function() {
        return {
          compare: function(actual) {
            const result = {};
            switch (type) {
              case 'Even':
                result.pass = actual % 2 === 0;
                result.message = `Expected ${actual} to be even.`;
                break;
              case 'Odd':
                result.pass = actual % 2 !== 0;
                result.message = `Expected ${actual} to be odd.`;
                break;
              case 'Prime':
                result.pass = actual > 1 && Array.from({length: actual - 2}, (_, i) => i + 2)
                        .every(i => actual % i !== 0);
                result.message = `Expected ${actual} to be prime.`;
                break;
            }
            return result;
          }
        };
      }
    });
  });
});

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

Использование функций с кастомными матчерами

В случаях, когда логика кастомного матчера требует выполнения асинхронных операций (например, проверка на основе данных с сервера), можно использовать async/await для обработки таких сценариев. Jasmine поддерживает асинхронные матчеры с использованием done или async:

beforeEach(function() {
  jasmine.addMatchers({
    toBeGreaterThanAsync: function() {
      return {
        compare: async function(actual, expected) {
          const result = {};
          const data = await fetchDataFromServer();  // Пример асинхронного вызова
          result.pass = data.value > expected;
          result.message = `Expected ${data.value} to be greater than ${expected}.`;
          return result;
        }
      };
    }
  });
});

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

Обработка ошибок кастомных матчеров

Если кастомный матчер выполнен некорректно, можно добавить обработку ошибок, чтобы обеспечить более информативные сообщения для пользователя. Например, если логика матча нарушена (например, в результате неверной проверки на простоту числа), можно бросить ошибку с детализированным сообщением:

beforeEach(function() {
  jasmine.addMatchers({
    toBeEvenAndGreaterThanTen: function() {
      return {
        compare: function(actual) {
          const result = {};
          if (actual % 2 !== 0) {
            throw new Error(`${actual} is not even.`);
          }
          if (actual <= 10) {
            throw new Error(`${actual} is not greater than 10.`);
          }
          result.pass = true;
          return result;
        }
      };
    }
  });
});

Теперь, если число не проходит проверку, будет выведено соответствующее сообщение об ошибке.

Поддержка кастомных матчеров в других фреймворках

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