Заглушки
Что такое заглушки?
Заглушки — это функции (шпионы) с предопределённым поведением.
Они поддерживают полный API шпионских тестов помимо методов, которые можно использовать для изменения поведения заглушки.
В качестве шпионов заглушки могут быть анонимными или обёрткой над существующими функциями. При обёртывании существующей функции заглушкой, исходная функция не вызывается.
Когда использовать заглушки?
Используйте заглушку, когда хотите:
-
Управлять поведением метода из теста, чтобы заставить код пройти по определённому пути. Примеры включают принудительное выброс исключения методом для проверки обработки ошибок.
-
Когда вы хотите предотвратить прямой вызов конкретного метода (возможно, потому что он вызывает нежелательное поведение, такое как
XMLHttpRequestили подобное).
Следующий пример — ещё один тест из PubSubJS, демонстрирующий создание анонимной заглушки, выбрасывающей исключение при вызове.
"test should call all subscribers, even if there are exceptions" : function(){
var message = 'an example message';
var stub = sinon.stub().throws();
var spy1 = sinon.spy();
var spy2 = sinon.spy();
PubSub.subscribe(message, stub);
PubSub.subscribe(message, spy1);
PubSub.subscribe(message, spy2);
PubSub.publishSync(message, undefined);
assert(spy1.called);
assert(spy2.called);
assert(stub.calledBefore(spy1));
}
Обратите внимание, как заглушка также реализует интерфейс шпиона. Тест проверяет, что все обратные вызовы были вызваны, а также что заглушка, выбрасывающая исключение, была вызвана до одного из других обратных вызовов.
Определение поведения заглушки при последовательных вызовах
Вызов методов определения поведения, таких как returns или throws несколько раз перезаписывает поведение заглушки. Начиная с версии Sinon 1.8, вы можете использовать метод onCall, чтобы заглушка отвечала по-разному на последовательные вызовы.
Обратите внимание, что в версиях Sinon с 1.5 по 1.7 несколько вызовов семейства методов yields* и callsArg* определяют последовательность поведения для последовательных вызовов. Начиная с версии 1.8, эта функциональность была удалена в пользу API onCall.
API заглушки
Свойства
var stub = sinon.stub();
Создаёт анонимную функцию-заглушку
var stub = sinon.stub(object, "method");
Заменяет object.method функцией-заглушкой. Если свойство не является функцией, выбрасывается исключение.
Исходную функцию можно восстановить, вызвав object.method.restore(); (или stub.restore();).
var stub = sinon.stub(object, "method", func);
Заменяет object.method на func, заключённую в spy.
Как обычно, object.method.restore(); можно использовать для восстановления исходного метода.
var stub = sinon.stub(obj);
Заглушает все методы объекта.
Обратите внимание, что обычно лучше заглушать отдельные методы, особенно для объектов, которые вы не понимаете или не контролируете все методы (например, зависимости библиотек).
Заглушка отдельных методов тестирует намерения более точно и менее восприимчива к непредвиденному поведению по мере развития кода объекта.
Если вы хотите создать заглушку объекта MyConstructor, но не хотите вызывать конструктор, используйте эту вспомогательную функцию.
var stub = sinon.createStubInstance(MyConstructor)
stub.withArgs(arg1[, arg2, ...]);
Заглушает метод только для предоставленных аргументов.
Это полезно для более выразительной формулировки утверждений, где вы можете получить доступ к шпиону с тем же вызовом. Это также полезно для создания заглушки, которая может по-разному реагировать на разные аргументы.
"test should stub method differently based on arguments": function () {
var callback = sinon.stub();
callback.withArgs(42).returns(1);
callback.withArgs(1).throws("TypeError");
callback(); // No return value, no exception
callback(42); // Returns 1
callback(1); // Throws TypeError
}
stub.onCall(n); Добавлено в v1.8
Определяет поведение заглушки при n-ом вызове. Полезно для тестирования последовательных взаимодействий.
"test should stub method differently on consecutive calls": function () {
var callback = sinon.stub();
callback.onCall(0).returns(1);
callback.onCall(1).returns(2);
callback.returns(3);
callback(); // Returns 1
callback(); // Returns 2
callback(); // All following calls return 3
}
Существуют методы onFirstCall, onSecondCall, onThirdCall, чтобы определения заглушек читались более естественно.
onCall можно комбинировать со всеми методами определения поведения в этом разделе. В частности, его можно использовать вместе с withArgs.
"test should stub method differently on consecutive calls with certain argument": function () {
var callback = sinon.stub();
callback.withArgs(42)
.onFirstCall().returns(1)
.onSecondCall().returns(2);
callback.returns(0);
callback(1); // Returns 0
callback(42); // Returns 1
callback(1); // Returns 0
callback(42); // Returns 2
callback(1); // Returns 0
callback(42); // Returns 0
}
Обратите внимание, как поведение заглушки для аргумента 42 возвращается к значению по умолчанию, когда больше вызовов не определено.
stub.onFirstCall();
Псевдоним для stub.onCall(0);
stub.onSecondCall();
Псевдоним для stub.onCall(1);
stub.onThirdCall();
Псевдоним для stub.onCall(2);
stub.reset();
Сбрасывает поведение и историю заглушки.
Это эквивалентно вызову как stub.resetBehavior(), так и stub.resetHistory()
Обновлено в sinon@2.0.0
stub.resetBehavior();
Сбрасывает поведение заглушки до значения по умолчанию
var stub = sinon.stub(); stub.returns(54) stub(); // 54 stub.resetBehavior(); stub(); // undefined
stub.resetHistory();
Сбрасывает историю заглушки
var stub = sinon.stub(); stub.called // false stub(); stub.called // true stub.resetHistory(); stub.called // false
С sinon@2.0.0
stub.callsFake(fakeFunction);
Заставляет заглушку вызывать предоставленную fakeFunction при вызове.
var myObj = {};
myObj.prop = function propFn() {
return 'foo';
};
sinon.stub(myObj, 'prop').callsFake(function fakeFn() {
return 'bar';
});
myObj.prop(); // 'bar'
stub.returns(obj);
Заставляет заглушку возвращать указанное значение.
stub.returnsArg(index);
Заставляет заглушку возвращать аргумент по указанному индексу.
stub.returnsArg(0); заставляет заглушку возвращать первый аргумент.
stub.returnsThis();
Заставляет заглушку возвращать своё значение this.
Полезно для заглушения jQuery-стилевых API «потоков действий».
stub.resolves(value);
Заставляет заглушку возвращать Promise, который разрешается до указанного значения.
При создании Promise sinon использует метод Promise.resolve. Вам необходимо предоставить полифилл в средах, не имеющих Promise. Библиотека Promise может быть переопределена с помощью метода usingPromise.
С sinon@2.0.0
stub.throws();
Заставляет заглушку выбросить исключение (Error).
stub.throws("TypeError");
Заставляет заглушку выбросить исключение указанного типа.
stub.throws(obj);
Заставляет заглушку выбросить предоставленный объект исключения.
stub.rejects();
Заставляет заглушку возвращать Promise, который отклоняется с исключением (Error).
При создании Promise sinon использует метод Promise.reject. Вам необходимо предоставить полифилл в средах, не имеющих Promise. Библиотека Promise может быть переопределена с помощью метода usingPromise.
С sinon@2.0.0
stub.rejects("TypeError");
Заставляет заглушку возвращать Promise, который отклоняется с исключением указанного типа.
С sinon@2.0.0
stub.rejects(value);
Заставляет заглушку возвращать Promise, который отклоняется с указанным объектом исключения.
С sinon@2.0.0
stub.callsArg(index);
Заставляет заглушку вызывать аргумент по указанному индексу как обратный вызов. stub.callsArg(0); заставляет заглушку вызывать первый аргумент как обратный вызов.
stub.callThrough();
Заставляет оригинальный метод, обернутый в заглушку, быть вызванным, если ни одна из условных заглушек не соответствует.
var stub = sinon.stub();
var obj = {};
obj.sum = function sum(a, b) {
return a + b;
};
stub(obj, 'sum');
obj.sum.withArgs(2, 2).callsFake(function foo() {
return 'bar';
});
obj.sum.callThrough();
obj.sum(2, 2); // 'bar'
obj.sum(1, 2); // 3
stub.callsArgOn(index, context);
Как stub.callsArg(index);, но с дополнительным параметром для передачи контекста this.
stub.callsArgWith(index, arg1, arg2, ...);
Как callsArg, но с аргументами для передачи обратного вызова.
stub.callsArgOnWith(index, context, arg1, arg2, ...);
Как выше, но с дополнительным параметром для передачи контекста this.
stub.usingPromise(promiseLibrary);
Заставляет заглушку возвращать promises, используя определённую библиотеку Promise вместо глобальной, при использовании stub.rejects или stub.resolves. Возвращает заглушку для возможности цепочки.
var myObj = {
saveSomething: sinon.stub().usingPromise(bluebird.Promise).resolves("baz");
}
myObj.saveSomething()
.tap(function(actual) {
console.log(actual); // baz
});
С sinon@2.0.0
stub.yields([arg1, arg2, ...])
Аналогично callsArg.
Заставляет заглушку вызывать первый полученный обратный вызов с предоставленными аргументами (если таковые имеются).
Если метод принимает более одного обратного вызова, вам необходимо использовать callsArg, чтобы заглушка вызывала обратные вызовы, отличные от первого.
stub.yieldsOn(context, [arg1, arg2, ...])
Как выше, но с дополнительным параметром для передачи контекста this.
stub.yieldsTo(property, [arg1, arg2, ...])
Заставляет шпиона вызывать обратный вызов, переданный в качестве свойства объекта шпиону.
Как yields, yieldsTo получает первый соответствующий аргумент, находит обратный вызов и вызывает его с (необязательными) аргументами.
stub.yieldsToOn(property, context, [arg1, arg2, ...])
Как выше, но с дополнительным параметром для передачи контекста this.
"test should fake successful ajax request": function () {
sinon.stub(jQuery, "ajax").yieldsTo("success", [1, 2, 3]);
jQuery.ajax({
success: function (data) {
assertEquals([1, 2, 3], data);
}
});
}
stub.yield([arg1, arg2, ...])
Вызывает обратные вызовы, переданные в stub с заданными аргументами.
Если заглушка никогда не вызывалась с аргументом-функцией, yield выбросит ошибку.
Также алиас для invokeCallback.
stub.yieldTo(callback, [arg1, arg2, ...])
Вызывает обратные вызовы, переданные в виде свойства объекта заглушке.
Как yield, yieldTo получает первый соответствующий аргумент, находит обратный вызов и вызывает его с (необязательными) аргументами.
"calling callbacks": function () {
var callback = sinon.stub();
callback({
"success": function () {
console.log("Success!");
},
"failure": function () {
console.log("Oh noes!");
}
});
callback.yieldTo("failure"); // Logs "Oh noes!"
}
stub.callArg(argNum)
Как yield, но с явным номером аргумента, определяющим, какой обратный вызов вызывать.
Полезно, если функция вызывается с несколькими обратными вызовами, а просто вызов первого обратного вызова нежелателен.
"calling the last callback": function () {
var callback = sinon.stub();
callback(function () {
console.log("Success!");
}, function () {
console.log("Oh noes!");
});
callback.callArg(1); // Logs "Oh noes!"
}
stub.callArgWith(argNum, [arg1, arg2, ...])
Как callArg, но с аргументами.
stub.callsArgAsync(index);
То же самое, что и их соответствующие не-асинхронные аналоги, но с отложенным обратным вызовом (выполняется не сразу, а после короткой задержки и в другой «потоке»)
stub.callsArgAsync(index);
stub.callsArgOnAsync(index, context);
stub.callsArgWithAsync(index, arg1, arg2, ...);
stub.callsArgOnWithAsync(index, context, arg1, arg2, ...);
stub.yieldsAsync([arg1, arg2, ...]);
stub.yieldsOnAsync(context, [arg1, arg2, ...]);
stub.yieldsToAsync(property, [arg1, arg2, ...]);
stub.yieldsToOnAsync(property, context, [arg1, arg2, ...])
Аналогично своим соответствующим синхронным аналогам, но с отложенным вызовом обратного вызова (выполняется не сразу, а после короткой задержки и в другой «потоке»)
sinon.addBehavior(name, fn);
Добавить пользовательское поведение. Имя будет доступно в качестве функции в подстановках, и механизм цепочки вызовов будет настроен за вас (например, не нужно возвращать ничего из вашей функции, её значение возврата будет проигнорировано). fn будет передавать подставленную инстанцию в качестве первого аргумента, а затем аргументы пользователя.
const sinon = require('sinon');
sinon.addBehavior('returnsNum', (fake, n) => fake.returns(n));
var stub = sinon.stub().returnsNum(42);
assert.equals(stub(), 42);
stub.get(getterFn)
Заменяет новый метод получения для этой подстановки.
var myObj = {
prop: 'foo'
};
sinon.stub(myObj, 'prop').get(function getterFn() {
return 'bar';
});
myObj.prop; // 'bar'
stub.set(setterFn)
Определяет новый метод задания для этой подстановки.
var myObj = {
example: 'oldValue',
prop: 'foo'
};
sinon.stub(myObj, 'prop').set(function setterFn(val) {
myObj.example = val;
});
myObj.prop = 'baz';
myObj.example; // 'baz'
stub.value(newVal)
Определяет новое значение для этой подстановки.
var myObj = {
example: 'oldValue',
};
sinon.stub(myObj, 'example').value('newValue');
myObj.example; // 'newValue'
Вы можете восстановить значения, вызвав метод restore:
var myObj = {
example: 'oldValue',
};
var stub = sinon.stub(myObj, 'example').value('newValue');
stub.restore()
myObj.example; // 'oldValue'
© 2010–2017 Christian Johansen
Licensed under the BSD License.
http://sinonjs.org/releases/v2.4.1/stubs