Stubbing является важной техникой в тестировании, позволяющей имитировать поведение зависимостей в тестах. Это особенно полезно, когда необходимо протестировать компонент или функцию, которые делают запросы к внешним ресурсам, таким как API или базы данных. Применение stubbing позволяет избежать реальных запросов во время тестирования, повысив скорость выполнения тестов и минимизируя внешние зависимости.
Запросы к внешним ресурсам могут быть медленными и ненадежными, что делает тестирование таких компонентов сложным. В процессе работы могут возникнуть проблемы с сетью, изменениями в API или даже ограничениями на количество запросов. Чтобы изолировать тесты от этих факторов, stubbing позволяет заменить реальные вызовы на фиктивные. Это позволяет:
Jasmine предоставляет мощные инструменты для замены реальных функций
или объектов на заглушки (stubs). Основная цель стуба — это подменить
настоящую реализацию функции или метода, чтобы она не выполняла реальные
действия, а возвращала заранее подготовленный результат. Для этого в
Jasmine используются такие методы, как spyOn и
and.callFake.
Предположим, что необходимо протестировать функцию, которая делает
HTTP-запрос через библиотеку fetch. Вместо того чтобы
выполнять реальный запрос, мы можем использовать stubbing, чтобы вернуть
заранее подготовленный ответ.
describe('Тестирование функции getUser', () => {
it('должен вернуть данные пользователя', () => {
// Создание подмены для метода fetch
spyOn(window, 'fetch').and.callFake(() => {
return Promise.resolve({
json: () => Promise.resolve({ name: 'Иван', age: 30 })
});
});
// Тестируемая функция
function getUser() {
return fetch('https://api.example.com/user')
.then(response => response.json());
}
getUser().then(data => {
expect(data.name).toBe('Иван');
expect(data.age).toBe(30);
});
});
});
В этом примере метод fetch заменяется на фиктивную
функцию с помощью spyOn. Вместо выполнения настоящего
HTTP-запроса, мы возвращаем объект с данными пользователя, который будет
использован в тесте. Такой подход позволяет эффективно тестировать код,
не зависимый от реальных внешних запросов.
spyOn и его
использованиеМетод spyOn в Jasmine позволяет не только подменять
методы объектов, но и отслеживать их вызовы. Это полезно для проверки
того, как часто и с какими параметрами были вызваны функции.
Пример:
describe('Проверка вызова fetch', () => {
it('должен вызвать fetch один раз', () => {
spyOn(window, 'fetch').and.callFake(() => Promise.resolve({ json: () => Promise.resolve({}) }));
fetch('https://api.example.com/data');
expect(fetch).toHaveBeenCalledTimes(1);
});
});
Здесь мы проверяем, что метод fetch был вызван ровно
один раз. Для этого мы используем toHaveBeenCalledTimes(1)
— матчере, который проверяет количество вызовов функции.
callFake и callThroughМетод callFake позволяет задать свою имплементацию для
подменяемой функции. Это дает возможность не только заменить функцию, но
и контролировать её поведение.
Пример с использованием callThrough:
describe('Пример с callThrough', () => {
it('должен вызвать оригинальную функцию', () => {
const originalFunction = jasmine.createSpy('original').and.callFake(() => 'fake result');
const result = originalFunction();
expect(originalFunction).toHaveBeenCalled();
expect(result).toBe('fake result');
});
});
Метод and.callThrough позволяет сохранить оригинальное
поведение функции, и при этом можно дополнительно подменить или изменить
ее результаты.
Когда тестируемая функция использует асинхронные операции, такие как
сетевые запросы, важно правильно работать с промисами или асинхронными
функциями. Для этого в Jasmine можно использовать такие методы, как
done, async/await или возвращение промиса.
Пример:
describe('Тестирование асинхронного запроса', () => {
it('должен правильно обрабатывать ответ от API', (done) => {
spyOn(window, 'fetch').and.callFake(() => {
return Promise.resolve({
json: () => Promise.resolve({ message: 'Success' })
});
});
fetch('https://api.example.com/status')
.then(response => response.json())
.then(data => {
expect(data.message).toBe('Success');
done(); // Завершаем тест
});
});
});
Здесь тест будет ждать завершения асинхронного запроса с помощью
функции done(). После выполнения всех операций будет
вызвана проверка, а затем завершение теста.
Когда работаете с реальными приложениями, важно помнить, что stubbing — это не всегда замена всех внешних запросов. В некоторых случаях может понадобиться использование моков или других техник для имитации сложных зависимостей, таких как базы данных, внешние сервисы или другие API.
Пример использования мока:
describe('Тестирование работы с API', () => {
it('должен обработать успешный ответ от API', () => {
const apiServiceMock = {
fetchData: jasmine.createSpy('fetchData').and.returnValue(Promise.resolve({ success: true }))
};
apiServiceMock.fetchData().then(data => {
expect(data.success).toBe(true);
});
});
});
Здесь мы создаем мок для сервиса, который должен вернуть успешный результат, и проверяем его поведение.
Stubbing может быть полезным, но важно понимать его ограничения. Например, чрезмерное использование stubbing может привести к тому, что тесты станут слишком изолированными от реального мира. Также стоит помнить, что stubbing должен использоваться в контексте изоляции компонентов и не должен заменять тестирование интеграций между системами.
Подмена внешних зависимостей может маскировать проблемы с интеграцией, которые возникают, когда компоненты взаимодействуют друг с другом в реальных условиях.