Заглушки
Что такое заглушки?
Тестовые заглушки — это функции (шпионы) с предварительно запрограммированным поведением.
Они поддерживают полный 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 заглушек
Если вам нужно заглушить геттеры/сеттеры или нефункциональные свойства, то следует использовать sandbox.stub
Свойства
var stub = sinon.stub();
Создает анонимную заглушку-функцию
var stub = sinon.stub(object, "method");
Заменяет object.method на заглушку-функцию. Исключение выбрасывается, если свойство еще не является функцией.
Исходную функцию можно восстановить, вызвав object.method.restore(); (или stub.restore();).
var stub = sinon.stub(object, "method", func);
var stub = sinon.stub(object, "method", func);Это было удалено из v3.0.0. Вместо этого следует использовать
stub(obj, 'meth').callsFake(fn)
Средство автоматической модификации кода доступно по адресу https://github.com/hurrymaplelad/sinon-codemod
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.throws(function() { return new Error(); });
Заставляет заглушку выбрасывать исключение, возвращенное функцией.
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);
Заставляет заглушку возвращать промисы, используя определенную библиотеку 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/v3.3.0/stubs