Заглушки
Что такое заглушки?
Заглушки — это функции (шпионы) с предварительно запрограммированным поведением.
Они поддерживают полный 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);
var stub = sinon.stub(object, "method", func);Это было удалено из v3.0.0. Вместо этого вы должны использовать
stub(obj, 'meth').callsFake(fn)
Доступен кодмод для обновления вашего кода
var stub = sinon.stub(obj);
Заглушает все методы объекта.
Обратите внимание, что обычно лучше заглушать отдельные методы, особенно для объектов, которые вы не понимаете или не контролируете все методы (например, зависимости библиотек).
Заглушение отдельных методов более точно отражает намерения и менее восприимчиво к неожиданному поведению по мере развития кода объекта.
Если вы хотите создать заглушку объекта MyConstructor, но не хотите вызывать конструктор, используйте эту вспомогательную функцию.
var stub = sinon.createStubInstance(MyConstructor, overrides);
overrides — это необязательная карта, переопределяющая созданные заглушки, например:
var stub = sinon.createStubInstance(MyConstructor, {
foo: sinon.stub().returnsThis()
});
эквивалентно:
var stub = sinon.createStubInstance(MyConstructor); stub.foo.returnsThis();
Если предоставленное значение не является заглушкой, оно будет использоваться в качестве возвращаемого значения:
var stub = sinon.createStubInstance(MyConstructor, {
foo: 3
});
эквивалентно:
var stub = sinon.createStubInstance(MyConstructor); stub.foo.returns(3);
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("name");
callback(); // No return value, no exception
callback(42); // Returns 1
callback(1); // Throws Error("name")
}
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
С sinon@5.0.0
Для удобства вы можете применить stub.reset() ко всем заглушкам, используя sinon.reset()
stub.resetBehavior();
Сбрасывает поведение заглушки до стандартного поведения
var stub = sinon.stub(); stub.returns(54) stub(); // 54 stub.resetBehavior(); stub(); // undefined
С sinon@5.0.0
Вы можете сбросить поведение всех заглушек, используя sinon.resetBehavior()
stub.resetHistory();
С sinon@2.0.0
Сбрасывает историю заглушки
var stub = sinon.stub(); stub.called // false stub(); stub.called // true stub.resetHistory(); stub.called // false
С sinon@5.0.0
Вы можете сбросить историю всех заглушек, используя sinon.resetHistory()
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); заставляет заглушку возвращать первый аргумент.
Если аргумент по указанному индексу недоступен, до sinon@6.1.2, будет возвращено значение undefined; начиная с sinon@6.1.2, будет выброшено TypeError.
stub.returnsThis();
Заставляет заглушку возвращать своё значение this.
Полезно для заглушения jQuery-подобных флюэнт API.
stub.resolves(value);
Заставляет заглушку возвращать Promise, который разрешается предоставленным значением.
При создании Promise, sinon использует метод Promise.resolve. Вы несёте ответственность за предоставление полифилла в средах, которые не предоставляют Promise. Библиотеку Promise можно переопределить, используя метод usingPromise.
С sinon@2.0.0
stub.resolvesArg(index);
Заставляет заглушку возвращать Promise, который разрешается аргументом по указанному индексу.
stub.resolvesArg(0); заставляет заглушку возвращать Promise, который разрешается первым аргументом.
Если аргумент по указанному индексу недоступен, будет выброшено TypeError.
С sinon@6.1.1
stub.throws();
Заставляет заглушку выбросить исключение (Error).
stub.throws("name"[, "optional message"]);
Заставляет заглушку выбросить исключение со свойством name установленным на предоставленную строку. Параметр message является необязательным и установит свойство message исключения.
stub.throws(obj);
Заставляет заглушку выбросить предоставленный объект исключения.
stub.throws(function() { return new Error(); });
Заставляет заглушку выбросить исключение, возвращённое функцией.
stub.throwsArg(index);
Заставляет заглушку выбросить аргумент по указанному индексу.
stub.throwsArg(0); заставляет заглушку выбросить первый аргумент как исключение.
Если аргумент по указанному индексу недоступен, будет выброшено TypeError.
С sinon@2.3.0
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); заставляет заглушку вызвать первый аргумент как обратный вызов.
Если аргумент по указанному индексу недоступен или не является функцией, будет выброшено TypeError.
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.callThroughWithNew();`
Causes the original method wrapped into the stub to be called using the `new` operator when none of the conditional stubs are matched.
```javascript
var obj = {};
obj.Sum = function MyConstructor(a, b) {
this.result = a + b;
};
sinon
.stub(obj, 'Sum')
.callThroughWithNew()
.withArgs(1, 2)
.returns({ result: 9000 });
(new obj.Sum(2, 2)).result; // 4
(new obj.Sum(1, 2)).result; // 9000
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.
Заставляет заглушку вызвать первый обратный вызов, полученный с предоставленными аргументами (если таковые имеются).
Если метод принимает более одного обратного вызова, вам нужно использовать yieldsRight для вызова последнего обратного вызова или callsArg для вызова других обратных вызовов, кроме первого или последнего.
stub.yieldsRight([arg1, arg2, ...])
Как yields , но вызывает последний полученный обратный вызов.
stub.yieldsOn(context, [arg1, arg2, ...])
Как yields, но с дополнительным параметром для передачи контекста 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, но с аргументами.
Асинхронные вызовы
То же самое, что и соответствующие им не асинхронные аналоги, но с отложенным обратным вызовом, вызываемым после обработки всех инструкций в текущей стеке вызовов.
- В среде Node обратный вызов откладывается с помощью
process.nextTick. - В браузере обратный вызов откладывается с помощью
setTimeout(callback, 0).
Дополнительная информация:
- https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick,
- https://developer.mozilla.org/ru/docs/Web/JavaScript/EventLoop,
- https://developer.mozilla.org/ru/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout.
stub.callsArgAsync(index);
Асинхронная версия stub.callsArg(index). См. также Асинхронные вызовы.
stub.callsArgOnAsync(index, context);
Асинхронная версия stub.callsArgOn(index, context). См. также Асинхронные вызовы.
stub.callsArgWithAsync(index, arg1, arg2, ...);
Асинхронная версия stub.callsArgWith(index, arg1, arg2, …). См. также Асинхронные вызовы.
stub.callsArgOnWithAsync(index, context, arg1, arg2, ...);
Асинхронная версия stub.callsArgOnWith(index, context, arg1, arg2, …). См. также Асинхронные вызовы.
stub.yieldsAsync([arg1, arg2, ...]);
Асинхронная версия stub.yields([arg1, arg2, …]). См. также Асинхронные вызовы.
stub.yieldsOnAsync(context, [arg1, arg2, ...]);
Асинхронная версия stub.yieldsOn(context, [arg1, arg2, …]). См. также Асинхронные вызовы.
stub.yieldsToAsync(property, [arg1, arg2, ...]);
Асинхронная версия stub.yieldsTo(property, [arg1, arg2, …]). См. также Асинхронные вызовы.
stub.yieldsToOnAsync(property, context, [arg1, arg2, ...])
Асинхронная версия stub.yieldsToOn(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–2020 Christian Johansen
Licensed under the BSD License.
https://sinonjs.org/releases/v7.5.0/stubs