Шпионы
Введение
Что такое шпион-тест?
Шпион-тест — это функция, которая записывает аргументы, возвращаемое значение, значение 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, "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 boolean, а также метод 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- список строковых представлений вызовов шпиона, при этом каждый вызов предваряется новой строкой и четырьмя пробелами
%t- список значений
this, переданных шпиону, разделённых запятыми %n- форматированное значение n-го аргумента, переданного
printf %*- список (нестроковых) аргументов, переданных
printf, разделённых запятыми %D- многострочный список аргументов, полученных всеми вызовами шпиона
© 2010–2018 Christian Johansen
Licensed under the BSD License.
http://sinonjs.org/releases/v4.5.0/spies