Spies

Introduction

What is a test spy?

A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. There are two types of spies: Some are anonymous functions, while others wrap methods that already exist in the system under test.

Creating a spy as an anonymous function

When the behavior of the spied-on function is not under test, you can use an anonymous function spy. The spy won’t do anything except record information about its calls. A common use case for this type of spy is testing how a function handles a callback, as in the following simplified example:

"test should call subscribers on publish": function () {
    var callback = sinon.spy();
    PubSub.subscribe("message", callback);

    PubSub.publishSync("message");

    assertTrue(callback.called);
}

Using a spy to wrap an existing method

sinon.spy(object, "method") creates a spy that wraps the existing function object.method. The spy will behave exactly like the original method (including when used as a constructor), but you will have access to data about all calls. The following is a slightly contrived example:

{
    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);
    }
}

Creating spies: sinon.spy() Method Signatures

var spy = sinon.spy();
Creates an anonymous function that records arguments, this value, exceptions and return values for all calls.
var spy = sinon.spy(myFunc);
Spies on the provided function
var spy = sinon.spy(object, "method");
Creates a spy for object.method and replaces the original method with the spy. An exception is thrown if the property is not already a function. The spy acts exactly like the original method in all cases. The original method can be restored by calling object.method.restore(). The returned spy is the function object which replaced the original method. spy === object.method.

Spy API

Spies provide a rich interface to inspect their usage. The above examples showed the calledOnce boolean property as well as the getCall method and the returned object’s args property. There are three ways of inspecting call data.

The preferred approach is to use the spy’s calledWith method (and friends) because it keeps your test from being too specific about which call did what and so on. It will return true if the spy was ever called with the provided arguments.

"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));
}

If you want to be specific, you can directly check the first argument of the first call. There are two ways of achieving this:

"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]);
}

The first example uses the two-dimensional args array directly on the spy, while the second example fetches the first call object and then accesses its args array. Which one to use is a matter of preference, but the recommended approach is going with spy.calledWith(arg1, arg2, ...) unless there’s a need to make the tests highly specific.

API

Spy objects are objects returned from sinon.spy(). When spying on existing methods with sinon.spy(object, method), the following properties and methods are also available on object.method.

Properties

spy.withArgs(arg1[, arg2, ...]);

Creates a spy that only records calls when the received arguments match those passed to withArgs. This is useful to be more expressive in your assertions, where you can access the spy with the same call.

"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

The number of recorded calls.

spy.called

true if the spy was called at least once

spy.notCalled

true if the spy was not called

spy.calledOnce

true if spy was called exactly once

spy.calledTwice

true if the spy was called exactly twice

spy.calledThrice

true if the spy was called exactly thrice

spy.firstCall

The first call

spy.secondCall

The second call

spy.thirdCall

The third call

spy.lastCall

The last call

spy.calledBefore(anotherSpy);

Returns true if the spy was called before anotherSpy

spy.calledAfter(anotherSpy);

Returns true if the spy was called after anotherSpy

spy.calledImmediatelyBefore(anotherSpy);

Returns true if spy was called before anotherSpy, and no spy calls occurred between spy and anotherSpy.

spy.calledImmediatelyAfter(anotherSpy);

Returns true if spy was called after anotherSpy, and no spy calls occurred between anotherSpy and spy.

spy.calledOn(obj);

Returns true if the spy was called at least once with obj as this. calledOn also accepts a matcher spyCall.calledOn(sinon.match(fn)) (see matchers).

spy.alwaysCalledOn(obj);

Returns true if the spy was always called with obj as this.

spy.calledWith(arg1, arg2, ...);

Returns true if spy was called at least once with the provided arguments.

Can be used for partial matching, Sinon only checks the provided arguments against actual arguments, so a call that received the provided arguments (in the same spots) and possibly others as well will return true.

spy.calledOnceWith(arg1, arg2, ...);

Returns true if spy was called at exactly once with the provided arguments.

spy.alwaysCalledWith(arg1, arg2, ...);

Returns true if spy was always called with the provided arguments (and possibly others).

spy.calledWithExactly(arg1, arg2, ...);

Returns true if spy was called at least once with the provided arguments and no others.

spy.calledOnceWithExactly(arg1, arg2, ...);

Returns true if spy was called exactly once with the provided arguments and no others.

spy.alwaysCalledWithExactly(arg1, arg2, ...);

Returns true if spy was always called with the exact provided arguments.

spy.calledWithMatch(arg1, arg2, ...);

Returns true if spy was called with matching arguments (and possibly others).

This behaves the same as spy.calledWith(sinon.match(arg1), sinon.match(arg2), ...).

spy.alwaysCalledWithMatch(arg1, arg2, ...);

Returns true if spy was always called with matching arguments (and possibly others).

This behaves the same as spy.alwaysCalledWith(sinon.match(arg1), sinon.match(arg2), ...).

spy.calledWithNew();

Returns true if spy/stub was called the new operator.

Beware that this is inferred based on the value of the this object and the spy function’s prototype, so it may give false positives if you actively return the right kind of object.

spy.neverCalledWith(arg1, arg2, ...);

Returns true if the spy/stub was never called with the provided arguments.

spy.neverCalledWithMatch(arg1, arg2, ...);

Returns true if the spy/stub was never called with matching arguments.

This behaves the same as spy.neverCalledWith(sinon.match(arg1), sinon.match(arg2), ...).

spy.threw();

Returns true if spy threw an exception at least once.

spy.threw("TypeError");

Returns true if spy threw an exception of the provided type at least once.

spy.threw(obj);

Returns true if spy threw the provided exception object at least once.

spy.alwaysThrew();

Returns true if spy always threw an exception.

spy.alwaysThrew("TypeError");

Returns true if spy always threw an exception of the provided type.

spy.alwaysThrew(obj);

Returns true if spy always threw the provided exception object.

spy.returned(obj);

Returns true if spy returned the provided value at least once.

Uses deep comparison for objects and arrays. Use spy.returned(sinon.match.same(obj)) for strict comparison (see matchers).

spy.alwaysReturned(obj);

Returns true if spy always returned the provided value.

var spyCall = spy.getCall(n);

Returns the nth call.

Accessing individual calls helps with more detailed behavior verification when the spy is called more than once.

sinon.spy(jQuery, "ajax");
jQuery.ajax("/stuffs");
var spyCall = jQuery.ajax.getCall(0);

assertEquals("/stuffs", spyCall.args[0]);

var spyCalls = spy.getCalls();

Returns an Array of all calls recorded by the spy.

spy.thisValues

Array of this objects, spy.thisValues[0] is the this object for the first call.

spy.args

Array of arguments received, spy.args[0] is an array of arguments received in the first call.

spy.exceptions

Array of exception objects thrown, spy.exceptions[0] is the exception thrown by the first call.

If the call did not throw an error, the value at the call’s location in .exceptions will be undefined.

spy.returnValues

Array of return values, spy.returnValues[0] is the return value of the first call.

If the call did not explicitly return a value, the value at the call’s location in .returnValues will be undefined.

spy.resetHistory();

Resets the state of a spy.

spy.restore();

Replaces the spy with the original method. Only available if the spy replaced an existing method.

spy.printf("format string", [arg1, arg2, ...]);

Returns the passed format string with the following replacements performed:

%n
the name of the spy "spy" by default)
%c
the number of times the spy was called, in words ("once", "twice", etc.)
%C
a list of string representations of the calls to the spy, with each call prefixed by a newline and four spaces
%t
a comma-delimited list of this values the spy was called on
%n
the formatted value of the nth argument passed to printf
%*
a comma-delimited list of the (non-format string) arguments passed to printf
%D
a multi-line list of the arguments received by all calls to the spy

© 2010–2018 Christian Johansen
Licensed under the BSD License.
http://sinonjs.org/releases/v5.1.0/spies