Mocha предоставляет гибкие возможности для тестирования кода на JavaScript, включая возможность мокирования зависимостей. Мокирование используется для имитации поведения внешних модулей или компонентов, которые могут быть сложными для тестирования или имеют побочные эффекты. Это позволяет сосредоточиться на тестировании логики, не затрагивая реальных зависимостей. В Mocha для мокирования часто применяются такие библиотеки, как Sinon.js, однако возможны и другие подходы.
Мокирование (mocking) — это процесс замены реальных объектов или функций их фиктивными версиями, которые имитируют поведение оригинала. В отличие от стубов (stubs), которые просто имитируют вызовы функций, моки позволяют задавать ожидаемое поведение и проверять, как они используются в тестах.
Мокирование необходимо для:
Для мокирования в Mocha часто используется библиотека Sinon.js, которая предлагает несколько инструментов: стабы, моки, шпионы.
Для использования Sinon.js в проекте, его необходимо установить через npm:
npm install sinon --save-dev
После этого можно подключить Sinon в файлах тестов:
const sinon = require('sinon');
Для создания мока используется метод sinon.mock(). Моки
позволяют задавать ожидаемое поведение и проверять, как были
использованы функции.
Пример:
const sinon = require('sinon');
describe('Мокирование с Sinon', function () {
it('должен проверять, был ли вызван метод', function () {
const myObj = {
myMethod: function () {
return 'Hello, world!';
}
};
const mock = sinon.mock(myObj);
mock.expects('myMethod').once().returns('Mocked value');
myObj.myMethod(); // Вызов метода
mock.verify(); // Проверка, что метод был вызван
});
});
В этом примере создается мок для метода myMethod,
который ожидает его вызов один раз и возвращает подмененное значение.
Метод mock.verify() проверяет, что все ожидания были
выполнены.
Для асинхронных функций моки могут ожидать промисы или другие асинхронные действия. Например:
const sinon = require('sinon');
describe('Мокирование асинхронных функций', function () {
it('должен правильно мокировать асинхронную функцию', async function () {
const myObj = {
fetchData: function () {
return new Promise((resolve) => resolve('Real data'));
}
};
const mock = sinon.mock(myObj);
mock.expects('fetchData').once().returns(Promise.resolve('Mocked data'));
const result = await myObj.fetchData();
mock.verify();
assert.equal(result, 'Mocked data');
});
});
В этом примере асинхронная функция fetchData возвращает
мокированный результат вместо настоящего.
Часто бывает необходимо мокировать не просто методы объектов, а целые
модули, которые импортируются в тестируемую единицу. В этом случае
используется функциональность библиотеки proxyquire или
встроенные средства, такие как require.cache.
proxyquire позволяет заменять зависимости в процессе
выполнения кода. Это полезно для мокирования импортированных
модулей.
Установка:
npm install proxyquire --save-dev
Пример использования:
const proxyquire = require('proxyquire');
const sinon = require('sinon');
const assert = require('assert');
describe('Мокирование модулей с Proxyquire', function () {
it('должен мокировать зависимость в модуле', function () {
const myDependency = sinon.stub().returns('Mocked value');
const myModule = proxyquire('./myModule', {
'./myDependency': myDependency
});
assert.equal(myModule(), 'Mocked value');
sinon.assert.calledOnce(myDependency);
});
});
Здесь мы используем proxyquire для замены реального
модуля myDependency на мок, который возвращает заранее
заданное значение.
Мокирование модуля также возможно путем замены кэшированных версий
модулей в require.cache. Это позволяет изменять поведение
модуля для тестов, не изменяя исходный код.
Пример:
const requireCache = require.cache;
const sinon = require('sinon');
const assert = require('assert');
describe('Мокирование с require.cache', function () {
it('должен мокировать модуль через require.cache', function () {
const originalModule = require('./myModule');
const mock = sinon.stub().returns('Mocked value');
// Мокируем модуль в require.cache
requireCache[require.resolve('./myModule')] = {
exports: mock
};
const result = require('./myModule');
assert.equal(result(), 'Mocked value');
});
});
Здесь мы изменяем кэшированный модуль myModule, заменяя
его мокированным вариантом.
Для мокирования сторонних библиотек можно использовать те же методы,
что и для обычных зависимостей. Это особенно полезно при тестировании
кода, который зависит от таких библиотек, как axios,
lodash и других.
Пример мокирования библиотеки axios с использованием
Sinon.js:
const axios = require('axios');
const sinon = require('sinon');
const assert = require('assert');
describe('Мокирование axios', function () {
it('должен мокировать запросы axios', async function () {
const mock = sinon.stub(axios, 'get').resolves({ data: 'Mocked data' });
const response = await axios.get('https://example.com');
assert.equal(response.data, 'Mocked data');
mock.restore(); // Восстановление оригинальной функции
});
});
В данном примере используется метод sinon.stub() для
замены метода axios.get() на мок, который всегда возвращает
заданные данные.
После мокирования можно проверять, как часто были вызваны функции, с
какими аргументами. Это достигается с помощью методов
expects, calledOnce, calledWith и
других.
Пример:
const sinon = require('sinon');
const assert = require('assert');
describe('Проверка вызовов с Sinon', function () {
it('должен проверять количество вызовов и аргументы', function () {
const myFunc = sinon.stub();
myFunc('argument1', 'argument2');
sinon.assert.calledOnce(myFunc);
sinon.assert.calledWith(myFunc, 'argument1', 'argument2');
});
});
Здесь мы проверяем, что функция была вызвана один раз с конкретными аргументами.
Мокирование в Mocha с помощью Sinon.js и других инструментов предоставляет мощные возможности для изоляции тестируемого кода и контроля над его зависимостями. Это позволяет сосредоточиться на логике работы тестируемого компонента и упрощает процесс тестирования, особенно в случаях, когда взаимодействие с внешними сервисами или компонентами затруднено.