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

Очень распространенный подход при работе с sinon, как показано в их собственной документации, - это вызов функции restore как часть хука afterEach Mocha.

afterEach(function () {
    // completely restore all fakes created through the sandbox
    sandbox.restore();
});

Этого должно быть достаточно, чтобы убедиться, что все вернется в норму, когда начнется новый тест, и они не влияют друг на друга, верно? Что ж, это иногда не так.

Давайте посмотрим на следующий пример:

Похоже, restore отлично справился со своей задачей, за исключением счетчика вызовов log.info шпиона. Мы ожидали, что он будет вызван только один раз в рамках второго модульного теста, но оказалось, что счетчик вызовов из предыдущего модульного теста не был должным образом сброшен, а значение callCount шпиона равно 3.

Почему sinon.restore не работает так, как я ожидал?

Если мы посмотрим на документацию по restore функции шпиона sinon, мы прочитаем следующее:

Заменяет шпиона исходным методом. Доступно только в том случае, если шпион заменил существующий метод.

Выделенное предложение является ключевым. Функции restore в sinon работают только со шпионами и заглушками, которые заменяют существующий метод, а наш шпион был создан как анонимный (автономный) шпион.

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

Как заставить его работать?

Знакомство с функциями сброса и сброса истории

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

  • resetHistory: для шпионов сбрасывает состояние; для заглушек сбрасывает вызов истории. Для песочницы сбрасывается история всех заглушек, созданных в песочнице.
  • resetBehavior: доступно только для заглушек и песочниц, сбрасывает исходное поведение заглушки.
  • reset: для заглушек сбрасывает поведение и историю; для песочницы сбрасывает внутреннее состояние всего, что создано в песочнице.

В нашем примере мы можем использовать функцию resetHistory, чтобы убедиться, что счетчик вызовов шпиона сбрасывается после первого теста, чтобы его выполнение не противоречило второму тесту. Мы можем сделать это как часть afterEach хука Mocha:

afterEach(() => {
    sandbox.restore();
    log.info.resetHistory();
});

Имейте в виду: добавление sandbox.reset() в ловушку afterEach не поможет, но это сработает, если мы добавим его в конец первого модульного теста - до сих пор не поняли, почему это происходит.

Или просто избегайте использования анонимных шпионов

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

const log = {
    info: sandbox.spy(),
    error: sandbox.spy()
};

Создайте общий фиктивный объект журнала и установите на него шпионов:

const log = {
    info: function () { /* Do nothing */ },
    error: function () { /* Do nothing */ }
};
sandbox.spy(log, 'info');
sandbox.spy(log, 'error');