Создание моков

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

Моки в контексте тестирования

Мок – это объект, который имитирует поведение настоящего объекта, но с заранее заданной логикой. Это позволяет тестировать компоненты системы, не взаимодействуя с реальными внешними зависимостями, такими как базы данных, API или другие сервисы. Моки обычно используются для:

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

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

В Mocha часто используется библиотека Sinon для создания моков, шпионов и стабов. Она предоставляет удобные инструменты для создания мок-объектов, которые можно настраивать для тестирования взаимодействий.

Установка Sinon

Для использования Sinon в Mocha необходимо установить его через npm:

npm install sinon --save-dev

Основы работы с Sinon

Sinon предоставляет несколько типов объектов для тестирования:

  1. Моки (Mocks) — используются для проверки вызовов методов с ожидаемыми аргументами.
  2. Шпионы (Spies) — позволяют отслеживать вызовы функций, не изменяя их поведения.
  3. Стабы (Stubs) — заменяют реальные функции на фиктивные, чтобы контролировать их поведение.

Создание моков с Sinon

Чтобы создать мок в Sinon, используется метод sinon.mock. Моки могут быть привязаны к объектам, и их можно настраивать для проверки того, как часто и с какими параметрами был вызван метод.

Пример создания мока:

const sinon = require('sinon');

describe('Тестирование моков', function() {
  it('должен вызывать метод 1 раз', function() {
    const obj = {
      method: function() {}
    };

    const mock = sinon.mock(obj);
    mock.expects('method').once();

    obj.method();
    
    mock.verify();
  });
});

В этом примере создается объект с методом method, и мы ожидаем, что он будет вызван ровно один раз. После вызова метода проверяется, был ли он вызван, с помощью метода verify.

Настройка поведения моков

Моки также могут быть настроены для подмены возвращаемых значений. Для этого можно использовать метод withArgs и returns:

const sinon = require('sinon');

describe('Тестирование моков с подменой значений', function() {
  it('должен возвращать подмененное значение', function() {
    const obj = {
      method: function(x) {
        return x * 2;
      }
    };

    const mock = sinon.mock(obj);
    mock.expects('method').withArgs(2).returns(4);

    const result = obj.method(2);
    
    mock.verify();
    result.should.equal(4);
  });
});

В этом примере мы ожидаем, что метод method будет вызван с аргументом 2 и вернет 4.

Использование моков с асинхронными функциями

Для работы с асинхронными методами моки можно настроить с учетом задержек и промисов. Например:

const sinon = require('sinon');

describe('Тестирование асинхронных моков', function() {
  it('должен работать с асинхронными методами', function(done) {
    const obj = {
      asyncMethod: async function() {
        return 'done';
      }
    };

    const mock = sinon.mock(obj);
    mock.expects('asyncMethod').resolves('done');

    obj.asyncMethod().then((result) => {
      result.should.equal('done');
      mock.verify();
      done();
    });
  });
});

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

Сравнение моков и стабов

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

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

const sinon = require('sinon');

describe('Тестирование стаба', function() {
  it('должен подменить метод на заглушку', function() {
    const obj = {
      method: function() {
        return 'original';
      }
    };

    const stub = sinon.stub(obj, 'method').returns('stubbed');
    
    const result = obj.method();
    
    result.should.equal('stubbed');
    
    stub.restore(); // Восстанавливаем оригинальный метод
  });
});

Здесь метод method заменяется на стаб, который возвращает заранее заданное значение, не проверяя, как и сколько раз он был вызван.

Применение моков в сложных сценариях

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

const sinon = require('sinon');

describe('Тестирование сложных взаимодействий', function() {
  it('должен проверить взаимодействие нескольких моков', function() {
    const obj1 = {
      fetchData: function() {
        return 'data';
      }
    };

    const obj2 = {
      processData: function(data) {
        return data.toUpperCase();
      }
    };

    const mock1 = sinon.mock(obj1);
    const mock2 = sinon.mock(obj2);

    mock1.expects('fetchData').returns('data');
    mock2.expects('processData').withArgs('data').returns('DATA');

    const result = obj2.processData(obj1.fetchData());
    
    result.should.equal('DATA');
    mock1.verify();
    mock2.verify();
  });
});

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

Резюме

Создание моков в Mocha с использованием библиотеки Sinon предоставляет мощные инструменты для изоляции компонентов и проверки их взаимодействий. Моки позволяют подменять поведение объектов и проверять, как они взаимодействуют с другими частями системы. Правильное использование моков помогает писать более стабильные и изолированные тесты, что критически важно для поддержания качества кода.