Мокирование (mocking) — это техника, используемая в тестировании для изоляции части системы от её зависимостей. В Jasmine, как и в других фреймворках для тестирования, мокирование позволяет имитировать поведение зависимых модулей или объектов, которые могут быть слишком сложными, медленными или изменяющими состояние системы. Этот подход помогает фокусироваться на тестировании функционала в изоляции, без необходимости взаимодействовать с внешними сервисами, базами данных или API.
Мокирование в Jasmine реализуется с помощью spies — специальных объектов, которые подменяют реальные функции или методы. Spy позволяет отслеживать, как и с какими аргументами была вызвана функция, сколько раз она была вызвана и какой результат возвращён.
Создание простого spy:
const spy = jasmine.createSpy('spyName');
spy();
expect(spy).toHaveBeenCalled();
В примере выше создаётся простой spy, который ничего не возвращает и используется для проверки, был ли он вызван.
Часто тестируемые модули зависят от внешних функций или сервисов. Чтобы изолировать тестируемый код от таких зависимостей, можно использовать Jasmine для замены реальных функций на mock-функции. Это позволяет точно контролировать поведение зависимостей и предотвращать нежелательные побочные эффекты.
Пример мокирования метода объекта:
const myObject = {
fetchData: function() {
return 'real data';
}
};
spyOn(myObject, 'fetchData').and.returnValue('mocked data');
expect(myObject.fetchData()).toBe('mocked data');
В этом примере метод fetchData подменяется на мок,
который возвращает заранее заданное значение
'mocked data'.
Jasmine предоставляет несколько стандартных методов для настройки поведения spies:
and.returnValue(value) — возвращает
указанное значение при вызове.and.callThrough() — вызывает
оригинальную функцию, если spy используется для замены реальной
функции.and.callFake(fn) — позволяет задать
произвольное поведение через функцию fn, которая будет
выполнена вместо оригинала.and.throwError(error) — генерирует
ошибку при вызове метода.Пример использования нескольких методов spy:
const someService = {
fetchData: function() {
return 'real data';
}
};
// Создаём spy, который заменяет оригинальный метод
spyOn(someService, 'fetchData')
.and.callFake(() => {
return 'mocked data';
});
expect(someService.fetchData()).toBe('mocked data');
В этом примере используется callFake, чтобы задать свой
собственный способ выполнения метода.
Асинхронные функции требуют особого подхода при тестировании. Jasmine поддерживает работу с асинхронными методами, включая возможность мокирования промисов или коллбеков.
Пример мокирования асинхронной функции с использованием промисов:
const myAsyncService = {
fetchDataAsync: function() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('real data');
}, 1000);
});
}
};
// Мокируем асинхронную функцию
spyOn(myAsyncService, 'fetchDataAsync').and.returnValue(Promise.resolve('mocked data'));
myAsyncService.fetchDataAsync().then((data) => {
expect(data).toBe('mocked data');
});
Здесь используется метод and.returnValue, чтобы
возвратить разрешённый промис с результатом, который мы заранее
задали.
beforeEach и
afterEachДля управления состоянием зависимостей и очистки после тестов можно
использовать функции Jasmine beforeEach и
afterEach. Это удобно для настройки и удаления
mock-объектов между тестами.
Пример настройки и сброса моков:
describe('Testing with mocks', () => {
let mockDependency;
beforeEach(() => {
mockDependency = jasmine.createSpyObj('mockDependency', ['fetchData']);
mockDependency.fetchData.and.returnValue('mocked data');
});
it('should test the mocked dependency', () => {
expect(mockDependency.fetchData()).toBe('mocked data');
});
afterEach(() => {
// Очистка всех spy-объектов
jasmine.clock().uninstall();
});
});
Здесь используется createSpyObj, чтобы создать
mock-объект с несколькими методами, которые могут быть подменены с
помощью Jasmine.
Если тестируемый код использует классы или объекты, которые создаются
с помощью конструктора, можно замокать их с помощью
spyOn.
Пример мокирования конструктора класса:
class MyClass {
fetchData() {
return 'real data';
}
}
describe('Mocking class constructors', () => {
it('should mock the class constructor', () => {
const spyConstructor = spyOn(window, 'MyClass').and.returnValue({
fetchData: jasmine.createSpy().and.returnValue('mocked data')
});
const instance = new MyClass();
expect(instance.fetchData()).toBe('mocked data');
});
});
Этот подход полезен, если необходимо мокировать не только методы объекта, но и сам процесс создания экземпляров класса.
В случае работы с модулями, использующими ES6 import/export,
мокирование может быть немного более сложным. Для этого можно
использовать такие библиотеки, как jest или специальные
утилиты для мокирования модулей в Jasmine.
Пример мокирования модуля в Jasmine:
import { myFunction } from './myModule';
describe('Mocking ES6 Modules', () => {
it('should mock an imported function', () => {
spyOn(myFunction, 'myMethod').and.returnValue('mocked value');
expect(myFunction.myMethod()).toBe('mocked value');
});
});
Здесь требуется интеграция Jasmine с утилитами для мокирования модулей, так как стандартное Jasmine не предоставляет прямую поддержку для модулярных зависимостей.
Мокирование модульных зависимостей в Jasmine является мощным
инструментом для тестирования, который позволяет изолировать тестируемый
код от внешних зависимостей и сосредоточиться на его логике.
Использование spy, callFake, и других методов
позволяет точно контролировать поведение зависимостей и тестировать их
взаимодействие с основным кодом, не влияя на его работу.