В Jasmine partial mocking (частичное мокирование) объектов позволяет заменять только отдельные методы или свойства объекта, оставляя остальные нетронутыми. Этот подход полезен, когда необходимо протестировать часть функциональности объекта, не влияя на другие его методы, которые могут быть сложными или внешне зависимыми. В Jasmine этот механизм реализуется с помощью мока и шпионов, которые отслеживают вызовы определённых методов.
Jasmine предоставляет два основных инструмента для работы с моками и шпионами:
Для частичного мокирования используется именно шпион, так как он позволяет контролировать только определённые методы объекта, оставляя остальные неизменными.
Предположим, есть объект userService, который имеет
методы getUser(), saveUser() и
deleteUser(). Для тестирования нужно замокать только метод
getUser(), а остальные оставить без изменений. Пример
кода:
describe('UserService', function() {
var userService;
beforeEach(function() {
userService = {
getUser: function(id) {
return { id: id, name: 'John Doe' };
},
saveUser: function(user) {
// сложная логика сохранения
},
deleteUser: function(id) {
// сложная логика удаления
}
};
});
it('should mock only getUser method', function() {
// Создаём шпион на методе getUser
spyOn(userService, 'getUser').and.returnValue({ id: 1, name: 'Mocked User' });
var user = userService.getUser(1);
expect(user.name).toBe('Mocked User');
expect(userService.getUser).toHaveBeenCalledWith(1);
});
});
В этом примере создаётся объект userService с тремя
методами. Мы замещаем только метод getUser с помощью
spyOn, в то время как методы saveUser и
deleteUser остаются в своей оригинальной реализации. Это
позволяет проверить только изменённое поведение, не влияя на другие
функции объекта.
Метод spyOn в Jasmine предоставляет несколько важных
функций для конфигурирования поведения замещаемого метода:
Пример с использованием and.callFake:
describe('UserService', function() {
var userService;
beforeEach(function() {
userService = {
getUser: function(id) {
return { id: id, name: 'John Doe' };
},
saveUser: function(user) {
// сложная логика
},
deleteUser: function(id) {
// сложная логика
}
};
});
it('should mock getUser with custom logic', function() {
spyOn(userService, 'getUser').and.callFake(function(id) {
return { id: id, name: 'Custom Mocked User' };
});
var user = userService.getUser(2);
expect(user.name).toBe('Custom Mocked User');
});
});
Здесь мы использовали and.callFake для того, чтобы
написать кастомную логику возвращаемого значения, что может быть
полезно, когда нужно, чтобы мокированная функция выполняла более сложные
операции.
В случаях, когда нужно не только замокать часть методов, но и
сохранить оригинальные реализации других методов, стоит использовать
комбинацию шпионов и вызова оригинальных функций с помощью
and.callThrough().
Пример:
describe('UserService', function() {
var userService;
beforeEach(function() {
userService = {
getUser: function(id) {
return { id: id, name: 'John Doe' };
},
saveUser: function(user) {
// сложная логика
},
deleteUser: function(id) {
// сложная логика
}
};
});
it('should mock getUser and call saveUser', function() {
spyOn(userService, 'getUser').and.returnValue({ id: 1, name: 'Mocked User' });
spyOn(userService, 'saveUser').and.callThrough(); // Оставляем оригинальную реализацию
var user = userService.getUser(1);
expect(user.name).toBe('Mocked User');
userService.saveUser(user);
expect(userService.saveUser).toHaveBeenCalled();
});
});
В этом примере метод getUser замещается, а метод
saveUser остаётся в своей оригинальной реализации. Таким
образом, мы можем контролировать только необходимую часть поведения
объекта, а остальную логику оставить без изменений.
Частичное мокирование объектов в Jasmine предоставляет мощный инструмент для тестирования, позволяя контролировать только те части объекта, которые важны для конкретного теста, и оставлять остальную логику без изменений. Это упрощает процесс создания тестов, повышает их гибкость и улучшает читаемость кода, особенно когда объект имеет сложную или внешне зависимую логику.