How can I test sequence of function calls by Sinon.js?
Asked Answered
N

5

15

How can I test sequence of function calls by Sinon.js?

For example i have three (3) handlers in object, and want define sequence of handler calls. Is there any possibilities for this?

Neutrality answered 31/3, 2013 at 15:14 Comment(0)
N
25

http://sinonjs.org/docs/

sinon.assert.callOrder(spy1, spy2, ...)

Passes if the provided spies where called in the specified order.

Neutrality answered 31/3, 2013 at 18:29 Comment(4)
Using Function.prototype.apply(), you can call it with an array of spies like this: sinon.assert.callOrder.apply(sinon.assert, [spy1, spy2, spy3]);Recession
What about if I have one spy, and I want to assert that it was called first with an arg of 1 and second with an arg of 2?Mannerism
It looks like callOrder has been removed. The current approach is to use calledBefore and the related methods.Skedaddle
Current link to spy.calledBefore(anotherSpy);Spitter
M
3

For people who run into this looking for a way to define stubs with a specified behaviour on a call sequence. There is no direct way (as far as I've seen) to say: "This stub will be called X times, on the 1st call it will take parameter a and return b on the 2nd call it will take parameter c and return d ..." and so on.

The closest I found to this behaviour is:

  const expectation = sinon.stub()
            .exactly(N)
            .onCall(0)
            .returns(b)
            .onCall(1)
            .returns(d)
            // ...
            ;
   // Test that will end up calling the stub
   // expectation.callCount should be N
   // expectation.getCalls().map(call => call.args) should be [ a, b, ... ]

This way we can return specific values on each call in the sequence and then assert that the calls were made with the parameters we expected.

Maymaya answered 28/1, 2021 at 7:0 Comment(0)
A
1

There is also a sinon-chai plugin.

https://www.npmjs.com/package/sinon-chai-in-order

expect(spy).inOrder.to.have.been.calledWith(1)
                   .subsequently.calledWith(2)
                   .subsequently.calledWith(3);
Akmolinsk answered 11/10, 2020 at 13:48 Comment(0)
C
0

As Gajus mentioned callOrder() is no longer available and you can use callBefore(), calledAfter(), calledImmediatelyBefore() and calledImmediatelyAfter().

I found most convenient to assert sequential calls of one spy by getting all calls using spy.getCalls() and do assert.deepEqual() for call arguments.

Example - Assert order of console.log() calls.

// func to test
function testee() {
  console.log(`a`)
  console.log(`b`)
  console.log(`c`)
}

In your test case

const expected = [`a`, `b`, `c`]
const consoleSpy = spy(console, 'log')

testee()

const result = consoleSpy.getCalls().map(({ args }) => args[0])

assert.deepEqual(result, expected)
Crab answered 19/1, 2020 at 8:15 Comment(0)
S
0

Use spy.calledBefore(anotherSpy) to assert one spy is called before the other.

You can see an example of it in the stub documentation:

require("@fatso83/mini-mocha").install();

const sinon = require("sinon");
const PubSub = require("pubsub-js");
const referee = require("@sinonjs/referee");
const assert = referee.assert;

describe("PubSub", function () {
    it("should call all subscribers, even if there are exceptions", function () {
        const message = "an example message";
        const stub = sinon.stub().throws();
        const spy1 = sinon.spy();
        const spy2 = sinon.spy();
        const clock = sinon.useFakeTimers();

        PubSub.subscribe(message, stub);
        PubSub.subscribe(message, spy1);
        PubSub.subscribe(message, spy2);

        assert.exception(() => {
            PubSub.publishSync(message, "some data");

            // PubSubJS reschedules exceptions using setTimeout(fn,0)
            // We have faked the clock, so just tick the clock to throw!
            clock.tick(1);
        });

        assert.exception(stub);
        assert(spy1.called);
        assert(spy2.called);
        assert(stub.calledBefore(spy1)); // <----- HERE

        clock.restore();
    });
});

There are similar tools documented in that first link:

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.

Spitter answered 5/5 at 3:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.