sinon spy by example

Feb 15, 2014 00:00 · 1521 words · 8 minute read Programming JavaScript sinon

What is Sinon Spy ?

According to the xUnit patterns definition, test spy is designed to act as an observation point by recording the method calls made to it by the SUT (system under test) as it is exercised.

How Sinon Spy works?

You need to make test spy first to allow it observe the execution of your desire method and then assert your expectation with actual calls.

Examples of the Sinon Spy

I am trying to write different test cases for code below (order by easy to hard).

production code

production codeJavaScript

var sinonSpy = {
 
    callMyMethod: function (number, arg1, args2) {
        for (var i = 0; i < number; i++) {
            this.myMethod(arg1, args2);
        }
        this.firstMethod();
        this.secondMethod();
        this.doSomething();
    },
 
    myMethod: function (arg1, arg2) {
        //do something
    },
 
    firstMethod: function () {
        //do something
    },
 
    secondMethod: function () {
        //do something
    },
 
    doSomething: function () {
        //do something
    },
 
    TryCatchMethod: function (message) {
        try {
            this.exceptionMethod(message);
        } catch (err) {
        }
    },
 
    exceptionMethod: function (message) {
        throw  message;
    },
 
    returnMethod: function(someValue){
        return someValue;
    },
 
    print: function(arg1,arg2,arg3){
        //do something
    }
 
};

Test Code

Create sinon sandbox

In order to test each test case solely, we need to create our sandbox first to run the tests under it.

creating sandbox

SinonSpyTest = TestCase('SinonSpyTest');
 
SinonSpyTest.prototype.setUp = function () {
    this._sandbox = sinon.sandbox.create();
};
 
SinonSpyTest.prototype.tearDown = function () {
    this._sandbox.restore();
};

The sinon sandbox will be created before each test on the setup and will be teardown after each one.

Test sinon spy called count
spy called once

calledOnce

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodOneTimeWhenPassOneToIt = function () {
    expectAsserts(1);
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(1);
    assertTrue(myMethodSpy.calledOnce);
};

calledOnce will be assure that myMethod will be only call one time, during the code execution.

spy called twice

called twice

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodTwoTimesWhenPassTwoToIt = function () {
    expectAsserts(1);
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(2);
    assertTrue(myMethodSpy.calledTwice);
};

Similar to above method it will assure that myMethod will be only call twice, during the code execution.

spy called thrice

called thrice

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodThreeTimesWhenPassThreeToIt = function () {
    expectAsserts(1);
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(3);
    assertTrue(myMethodSpy.calledThrice);
};

Similar to above method it will assure that myMethod will be only call three times, during the code execution.

spy.callCount

call count

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesWhenPassFourToIt = function () {
    expectAsserts(1);
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4);
    assertEquals(4, myMethodSpy.callCount);
};

For more than three times, we can use callCount property to assure that our method will be called with our expected times.

Test sinon spy.withArgs

You can write more strict test by checking the input argument of your spied method.

Checking arguments of a method (String Type)

with args

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesWithExpectedArgsWhenPassFourToIt = function () {
    expectAsserts(1);
    var arg1 = 'arg number one';
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4, arg1, arg2);
    assertEquals(4, myMethodSpy.withArgs(arg1, arg2).callCount);
};

The above test will be assure that myMethod should be called four times with arg1 and arg2. Checking arguments of a method (None String Type)

with args none string

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesWithExpectedObjectArgsWhenPassFourToIt = function () {
    expectAsserts(1);
    var arg1 = ['1', '2', '3'];
    var arg2 = {a: 'a', b: 'b'};
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4, arg1, arg2);
    assertEquals(4, myMethodSpy.withArgs(arg1, arg2).callCount);
};

Similarly you can check more complex argument such as objects or arrays. bear in mind the reference of passed object and the assert one should be same otherwise you have check the values of the key pairs.

Sinon spy.CalledWith

calledWith

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesWithExpectedFirstArgWhenPassFourToIt = function () {
    expectAsserts(4);
    var arg1 = 'arg number one';
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4, arg1, arg2);
    assertEquals(4, myMethodSpy.withArgs(arg1).callCount);
    assertTrue(myMethodSpy.calledWith(arg1));
    assertTrue(myMethodSpy.neverCalledWith(undefined));
 
    var indexOfNumberOfTimeItGetCalled = 3;
    var indexOfArgThatPassesToTheMethod = 1
    assertEquals(arg2, myMethodSpy.args[indexOfNumberOfTimeItGetCalled][indexOfArgThatPassesToTheMethod]);
};
Spy.WithArgs & spy.CalledWith

These are only assert the existence of an args and would not match all the args 1 by 1.

Spy.neverCalledWith

Assert that the specific arg is not exist in the call.

Spy.args

The first argument will is nth call of the spy and the second one is the nth index arguments (both started from zero).

Sinon spy.calledWithExactly

calledWithExactly

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesWithExactExpectedArgsWhenPassFourToIt = function () {
    expectAsserts(2);
    var arg1 = 'arg number one';
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4, arg1, arg2);
    sinonSpy.callMyMethod(4, arg1);
    assertTrue(myMethodSpy.calledWithExactly(arg1, arg2));
    assertTrue(myMethodSpy.calledWithExactly(arg1, undefined));
};

calledWithExactly assert that the method called with all the args with 1 by 1 match . in the above example myMethod is called with arg1 and arg2 in the first time and arg1 and undefined on the second time.

Sinon spy.alwaysCalledWith

sinon always call with

SinonSpyTest.prototype.testCallMyMethodShouldCallMyMethodFourTimesAlwaysWithExactExpectedArgsWhenPassFourToIt = function () {
    expectAsserts(4);
    var arg1 = 'arg number one';
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(4, arg1, arg2);
    sinonSpy.callMyMethod(4, arg1);
    assertTrue(myMethodSpy.alwaysCalledWith(arg1));
    assertFalse(myMethodSpy.alwaysCalledWith(arg1, arg2));
    assertFalse(myMethodSpy.alwaysCalledWithExactly(arg1));
    assertFalse(myMethodSpy.alwaysCalledWithExactly(arg1, arg2));
};

alwaysCalledWith assure that myMethod argument always have expected value. therefore below assertion should be fail as it myMethod does not always has the arg2 argument.

alwaysCalledWith

assertFalse(myMethodSpy.alwaysCalledWith(arg1, arg2));

In the same way always called with exactly is check the argument in more strict way, and below assertion will be fail as not always myMethod get called with both or one arguments.

always called with exactly

assertFalse(myMethodSpy.alwaysCalledWithExactly(arg1));
assertFalse(myMethodSpy.alwaysCalledWithExactly(arg1, arg2));
Test sinon spy on specific call

spy nth call

SinonSpyTest.prototype.testMyMethodCalledWithExpectedElementsOnSecondCall = function () {
    expectAsserts(5);
    var arg1 = 'arg number one';
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(1, arg1, arg2);
    sinonSpy.callMyMethod(1, arg1);
    sinonSpy.callMyMethod(1);
    sinonSpy.callMyMethod(1, arg1);
    assertTrue(myMethodSpy.secondCall.calledWith(arg1, undefined));
    assertTrue(myMethodSpy.firstCall.calledWith(arg1, arg2));
    assertTrue(myMethodSpy.thirdCall.calledWith(undefined, undefined));
    assertTrue(myMethodSpy.lastCall.calledWith(arg1, undefined));
    assertTrue(myMethodSpy.getCall(3).calledWith(arg1, undefined));
 
};

It will check the specific call arguments and detail. firstCall, secondCall, thirdCall will the first, second and third call of a spied method respectively (obviously) ! if you are interested in asserting nth call you can use getCall(nth) method.

Test sinon call order

sinon call order

SinonSpyTest.prototype.testFirstMethodCalledBeforeSecondMethod = function () {
    expectAsserts(3);
    var firstMethodSpy = this._sandbox.spy(sinonSpy, 'firstMethod');
    var secondMethodSpy = this._sandbox.spy(sinonSpy, 'secondMethod');
    sinonSpy.callMyMethod();
    assertTrue(firstMethodSpy.calledBefore(secondMethodSpy));
    assertTrue(secondMethodSpy.calledAfter(firstMethodSpy));
    assertFalse(secondMethodSpy.calledBefore(firstMethodSpy));
};

In order to check that if a method call before or after another method you can use, calledBefore and calledAfter.

Test sinon spy calledOn

SinonSpyTest.prototype.testMyMethodCalledOnExpectedObject = function () {
    expectAsserts(2);
    var doSomethingSpy = this._sandbox.spy(sinonSpy, 'doSomething');
    sinonSpy.callMyMethod();
    assertTrue(doSomethingSpy.calledOn(sinonSpy));
    assertTrue(doSomethingSpy.alwaysCalledOn(sinonSpy));
};

it will assert the context of calling a method. in the above example it will assert that if doSomething method is called by the sinonSpy object or not.

Test sinon spy call with match

called with match

SinonSpyTest.prototype.testMyMethodCalledWithExpectedMatch = function () {
    expectAsserts(5);
    var arg1 = {one: 'arg number one'};
    var arg2 = 'arg number two';
    var myMethodSpy = this._sandbox.spy(sinonSpy, 'myMethod');
    sinonSpy.callMyMethod(1, arg1, arg2);
    sinonSpy.callMyMethod(1, arg1);
    assertTrue(myMethodSpy.calledWithMatch({}, ''));
    assertTrue(myMethodSpy.calledWithMatch({}));
    assertTrue(myMethodSpy.alwaysCalledWithMatch({}));
    assertFalse(myMethodSpy.alwaysCalledWithMatch({}, ''));
    assertTrue(myMethodSpy.neverCalledWithMatch([]));
};

called with match check if the arguments of the spied method contain our expected types. alwaysCalledWithMatch assert if all the calls contain our expected match. Therefore it will be fail for below assertion as it did not called with two args on the second call.

alwaysCalledWithMatch

assertFalse(myMethodSpy.alwaysCalledWithMatch({}, ''));

neverCalledWithMatch is just stand on the opposite side of the alwaysCalledWithMatch.

Test sinon spy exception

sinon spy exception

SinonSpyTest.prototype.testExceptionMethodThrowExpectedException = function () {
    expectAsserts(6);
    var exceptionMethodSpy = this._sandbox.spy(sinonSpy, 'exceptionMethod');
    var exceptionMessage = 'exception message';
    var exceptionObject = {detail: 'exception message'};
    sinonSpy.TryCatchMethod(exceptionMessage);
    sinonSpy.TryCatchMethod(exceptionObject);
    assertTrue(exceptionMethodSpy.threw());
    assertTrue(exceptionMethodSpy.threw(exceptionMessage));
    assertTrue(exceptionMethodSpy.threw(exceptionObject));
    assertFalse(exceptionMethodSpy.alwaysThrew(exceptionMessage));
    assertFalse(exceptionMethodSpy.alwaysThrew(exceptionObject));
    assertEquals(exceptionMessage, exceptionMethodSpy.exceptions[0]);
};

threw is assert that if the method throw an exception or not. you can check the exception message or object by passing it to it. alwaysThrew make sure if the method always throw expected message during the code execution. you can access to the exception message by exceptions[index of exception that catch during code execution].

Test sinon spy return

sinon spy return

SinonSpyTest.prototype.testReturnMethodShouldReturnsExpectedValue = function () {
    expectAsserts(4);
    var returnMethodSpy = this._sandbox.spy(sinonSpy, 'returnMethod');
    var returnMessage = 'some return message';
    var returnObject = {details: 'some return message'};
    sinonSpy.returnMethod(returnMessage);
    sinonSpy.returnMethod(returnObject);
    assertTrue(returnMethodSpy.returned(returnMessage));
    assertTrue(returnMethodSpy.returned(returnObject));
    assertFalse(returnMethodSpy.alwaysReturned(returnObject));
    assertEquals(returnMessage, returnMethodSpy.returnValues[0]);
};

it will assert that the spied method return expected string or object. alwaysReturned assert that the method returns expected value. you can access to the values of retuned value by returnValues[index of nth call].

Test sinon spy reset

sinon spy reset

SinonSpyTest.prototype.testResetSpyShouldResetIt = function () {
    expectAsserts(2);
    var doSomethingSpy = this._sandbox.spy(sinonSpy, 'doSomething');
    sinonSpy.doSomething();
    assertTrue(doSomethingSpy.called);
    doSomethingSpy.reset();
    assertFalse(doSomethingSpy.called);
};

it will reset a spy, so it will be fail for the below assertion.

reset

assertFalse(doSomethingSpy.called);
Sinon spy printf

printf will help us to get more detail about our test failure reason.

sinon spy printf

SinonSpyTest.prototype.testDoSomethingPrintSpyDetails = function () {
    expectAsserts(1);
    var doSomethingSpy = this._sandbox.spy(sinonSpy, 'print');
    var arg1 = {a: 'argument 1'};
    var arg2 = 2.33;
    var arg3 = 'argument 3';
    sinonSpy.print(arg1, arg2, arg3);
    assertTrue(true);
//    assertTrue(doSomethingSpy.printf("%n"),false); //name of method spy i.e. print
//    assertTrue(doSomethingSpy.printf("%c"),false); //number of times spy get called i.e. once
//    assertTrue(doSomethingSpy.printf("%C"),false); //print expected arguments that passed to it  i.e. { a: "argument 1" }, argument 2, argument 3)
//    assertTrue(doSomethingSpy.printf("%t"),false); //print list of values the spy was call on  i.e. methods of sinonSpy
};

“%n” will print the name of the method that we are spying on it.

“%c” will print the number of time that the spy get called in alphabetical way e.g once, twice etc.

“%C” will print all the arguments of the spied method.

“%t” will print the values of the object of the spied method.

Download

you can find the full API reference on sinon spy documentation . Also feel free to download the full source code of this example from my github.