Шпионы
Введение
Что такое шпион для тестирования?
Шпион для тестирования — это функция, которая записывает аргументы, возвращаемое значение, значение this и брошенное исключение (если есть) для всех своих вызовов. Существует два типа шпионов: некоторые являются анонимными функциями, а другие оборачивают методы, которые уже существуют в тестируемой системе.
Создание шпиона как анонимной функции
Когда поведение тестируемой функции не проверяется, вы можете использовать шпиона-анонимную функцию. Шпион ничего не сделает, кроме как запишет информацию о своих вызовах. Типичное применение такого шпиона — проверка того, как функция обрабатывает обратный вызов, как в следующем упрощенном примере:
"test should call subscribers on publish": function () {
var callback = sinon.spy();
PubSub.subscribe("message", callback);
PubSub.publishSync("message");
assertTrue(callback.called);
}
Использование шпиона для обертывания всех методов объекта
sinon.spy(object)
Шпионит за всеми методами объекта.
Обратите внимание, что обычно лучше шпионить за отдельными методами, особенно для объектов, методы которых вы не понимаете или не контролируете (например, библиотечные зависимости).
Шпионирование отдельных методов более точно проверяет намерения и менее подвержено неожиданному поведению по мере развития кода объекта.
Следующий пример немного искусственный:
{
sandbox: sinon.createSandbox(),
setUp: function () {
this.sandbox.spy(jQuery);
},
tearDown: function () {
this.sandbox.restore(); // Unwraps all spied methods
},
"test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
jQuery.getJSON("/some/resource");
assert(jQuery.ajax.calledOnce);
assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
}
}
Использование шпиона для обертывания существующего метода
sinon.spy(object, "method") создает шпиона, который оборачивает существующую функцию object.method. Шпион будет вести себя точно так же, как и исходный метод (включая использование в качестве конструктора), но у вас будет доступ к данным обо всех вызовах. Следующий пример немного искусственный:
{
setUp: function () {
sinon.spy(jQuery, "ajax");
},
tearDown: function () {
jQuery.ajax.restore(); // Unwraps the spy
},
"test should inspect jQuery.getJSON's usage of jQuery.ajax": function () {
jQuery.getJSON("/some/resource");
assert(jQuery.ajax.calledOnce);
assertEquals("/some/resource", jQuery.ajax.getCall(0).args[0].url);
assertEquals("json", jQuery.ajax.getCall(0).args[0].dataType);
}
}
Создание шпионов: sinon.spy() Подписи методов
var spy = sinon.spy();- Создаёт анонимную функцию, которая записывает аргументы,
thisзначение, исключения и возвращаемые значения для всех вызовов. var spy = sinon.spy(myFunc);- Шпионит за предоставленной функцией
var spy = sinon.spy(object, "method");- Создаёт шпиона для
object.methodи заменяет исходный метод шпионом. Если свойство не является функцией, бросается исключение. Шпион ведет себя точно как исходный метод во всех случаях. Исходный метод можно восстановить, вызвавobject.method.restore(). Возвращаемый шпион — это объект функции, который заменил исходный метод.spy === object.method.
API шпионов
Шпионы предоставляют богатый интерфейс для проверки их использования. В приведенных выше примерах показана calledOnce булева свойство, а также getCall метод и свойство args возвращаемого объекта. Существует три способа проверки данных вызовов.
Предпочтительный подход — использование метода calledWith шпиона (и его аналогов), так как это предотвращает чрезмерную специфичность вашего теста относительно того, какой вызов что делал и так далее. Он вернет true если шпион когда-либо вызывался с указанными аргументами.
"test should call subscribers with message as first argument" : function () {
var message = 'an example message';
var spy = sinon.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
assert(spy.calledWith(message));
}
Если вам нужна большая специфичность, вы можете напрямую проверить первый аргумент первого вызова. Существует два способа достижения этого:
"test should call subscribers with message as first argument" : function () {
var message = 'an example message';
var spy = sinon.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
assertEquals(message, spy.args[0][0]);
}
"test should call subscribers with message as first argument" : function () {
var message = 'an example message';
var spy = sinon.spy();
PubSub.subscribe(message, spy);
PubSub.publishSync(message, "some payload");
assertEquals(message, spy.getCall(0).args[0]);
}
Первый пример использует двумерный массив args напрямую в шпионе, а второй пример извлекает объект первого вызова, а затем обращается к массиву args. Какой из них использовать — дело вкуса, но рекомендуемый подход — использование spy.calledWith(arg1, arg2, ...) , если нет необходимости в высокой специфичности тестов.
API
Объекты шпионов являются объектами, возвращаемыми из sinon.spy(). При шпионировании существующих методов с sinon.spy(object, method), следующие свойства и методы также доступны для object.method.
Свойства
spy.withArgs(arg1[, arg2, ...]);
Создает шпиона, который записывает только вызовы, когда полученные аргументы соответствуют аргументам, переданным withArgs. Это полезно для большей выразительности в ваших утверждениях, где вы можете получить доступ к шпиону с тем же вызовом.
"should call method once with each argument": function () {
var object = { method: function () {} };
var spy = sinon.spy(object, "method");
object.method(42);
object.method(1);
assert(spy.withArgs(42).calledOnce);
assert(spy.withArgs(1).calledOnce);
}
spy.callCount
Количество записанных вызовов.
spy.called
true если шпион был вызван хотя бы один раз
spy.notCalled
true если шпион не был вызван
spy.calledOnce
true если шпион был вызван ровно один раз
spy.calledTwice
true если шпион был вызван ровно два раза
spy.calledThrice
true если шпион был вызван ровно три раза
spy.firstCall
Первый вызов
spy.secondCall
Второй вызов
spy.thirdCall
Третий вызов
spy.lastCall
Последний вызов
spy.calledBefore(anotherSpy);
Возвращает true если шпион был вызван до anotherSpy
spy.calledAfter(anotherSpy);
Возвращает true если шпион был вызван после anotherSpy
spy.calledImmediatelyBefore(anotherSpy);
Возвращает true если spy был вызван до anotherSpy, и никаких вызовов шпионов не произошло между spy и anotherSpy.
spy.calledImmediatelyAfter(anotherSpy);
Возвращает true если spy был вызван после anotherSpy, и никаких вызовов шпионов не произошло между anotherSpy и spy.
spy.calledOn(obj);
Возвращает true если шпион был вызван хотя бы один раз с obj как this. calledOn также принимает компаратор spyCall.calledOn(sinon.match(fn)) (см. сопоставители).
spy.alwaysCalledOn(obj);
Возвращает true если шпион всегда вызывался с obj как this.
spy.calledWith(arg1, arg2, ...);
Возвращает true если шпион был вызван как минимум один раз с предоставленными аргументами.
Можно использовать для частичного сопоставления, Sinon проверяет только предоставленные аргументы с фактическими аргументами, поэтому вызов, который получил предоставленные аргументы (в тех же позициях) и, возможно, другие аргументы, вернёт true.
spy.calledOnceWith(arg1, arg2, ...);
Возвращает true если шпион был вызван ровно один раз в общем и этот единственный вызов использовал предоставленные аргументы.
spy.alwaysCalledWith(arg1, arg2, ...);
Возвращает true если шпион всегда вызывался с предоставленными аргументами (и, возможно, другими).
spy.calledWithExactly(arg1, arg2, ...);
Возвращает true если шпион был вызван как минимум один раз с предоставленными аргументами и ни с какими другими.
spy.calledOnceWithExactly(arg1, arg2, ...);
Возвращает true если шпион был вызван ровно один раз и этот вызов использовал именно предоставленные аргументы и ни какие другие.
spy.alwaysCalledWithExactly(arg1, arg2, ...);
Возвращает true если шпион всегда вызывался именно с предоставленными аргументами.
spy.calledWithMatch(arg1, arg2, ...);
Возвращает true если шпион был вызван с соответствующими аргументами (и, возможно, другими).
Это ведет себя так же, как spy.calledWith(sinon.match(arg1), sinon.match(arg2), ...).
spy.alwaysCalledWithMatch(arg1, arg2, ...);
Возвращает true если шпион всегда вызывался с соответствующими аргументами (и, возможно, другими).
Это ведет себя так же, как spy.alwaysCalledWith(sinon.match(arg1), sinon.match(arg2), ...).
spy.calledWithNew();
Возвращает true если шпион/заглушка был вызван оператором new.
Обратите внимание, что это определяется на основе значения объекта this и функции шпиона prototype, поэтому это может давать ложные срабатывания, если вы активно возвращаете правильный тип объекта.
spy.neverCalledWith(arg1, arg2, ...);
Возвращает true если шпион/заглушка никогда не вызывался с предоставленными аргументами.
spy.neverCalledWithMatch(arg1, arg2, ...);
Возвращает true если шпион/заглушка никогда не вызывался с соответствующими аргументами.
Это ведет себя так же, как spy.neverCalledWith(sinon.match(arg1), sinon.match(arg2), ...).
spy.threw();
Возвращает true если шпион бросил исключение как минимум один раз.
spy.threw("TypeError");
Возвращает true если шпион бросил исключение указанного типа как минимум один раз.
spy.threw(obj);
Возвращает true если шпион бросил предоставленный объект исключения как минимум один раз.
spy.alwaysThrew();
Возвращает true если шпион всегда бросал исключение.
spy.alwaysThrew("TypeError");
Возвращает true если шпион всегда бросал исключение указанного типа.
spy.alwaysThrew(obj);
Возвращает true если шпион всегда бросал предоставленный объект исключения.
spy.returned(obj);
Возвращает true если шпион возвращал предоставленное значение как минимум один раз.
Использует глубокое сравнение для объектов и массивов. Используйте spy.returned(sinon.match.same(obj)) для строгого сравнения (см. сопоставители).
spy.alwaysReturned(obj);
Возвращает true если шпион всегда возвращал предоставленное значение.
var spyCall = spy.getCall(n);
Возвращает n-ый вызов.
Доступ к отдельным вызовам помогает более детально проверить поведение, когда шпион вызывается более одного раза.
sinon.spy(jQuery, "ajax");
jQuery.ajax("/stuffs");
var spyCall = jQuery.ajax.getCall(0);
assertEquals("/stuffs", spyCall.args[0]);
var spyCalls = spy.getCalls();
Возвращает Array всех вызовов, записанных шпионом.
spy.thisValues
Массив объектов this, spy.thisValues[0] — объект this для первого вызова.
spy.args
Массив полученных аргументов, spy.args[0] — массив аргументов, полученных при первом вызове.
spy.exceptions
Массив объектов исключений, сброшенных, spy.exceptions[0] — исключение, сброшенное первым вызовом.
Если вызов не сбросил ошибку, значение в позиции вызова в .exceptions будет undefined.
spy.returnValues
Массив возвращаемых значений, spy.returnValues[0] — возвращаемое значение первого вызова.
Если вызов не явно вернул значение, значение в позиции вызова в .returnValues будет undefined.
spy.resetHistory();
Сбрасывает состояние шпиона.
spy.restore();
Заменяет шпиона исходным методом. Доступно только если шпион заменил существующий метод.
spy.printf("format string", [arg1, arg2, ...]);
Возвращает переданную строку формата с выполненными следующими заменями:
%n- имя шпиона «шпион» по умолчанию)
%c- количество вызовов шпиона в словах («один раз», «дважды» и т.д.)
%C- список строковых представлений вызовов шпиона, с каждой строкой вызова, предваряемой новой строкой и четырьмя пробелами
%C- список значений аргументов вызовов шпиона, разделенных запятыми
%n- форматированное значение n-го аргумента, переданного
printf %*- список аргументов (не строки формата), переданных
printf %D- многострочный список аргументов, полученных всеми вызовами шпиона
© 2010–2020 Christian Johansen
Licensed under the BSD License.
https://sinonjs.org/releases/v7.5.0/spies