cschenio
cschenio

Reputation: 107

How to test async function with Sinon JS?

Below is a minimal example of what I want to achieve: I want to test fn3() being called if the async function fn2() resolved (when calling fn1). But I somehow failed to do that with sinon's stub syntax. I would like to know what I misunderstood.

// System Under Test

export function fn1() {
    fn2().then(() => {
        fn3();
    });
}

export async function fn2() {
    return new Promise((resolve, reject) => {
        // expensive work
        resolve();
    });
}

export function fn3() {
    console.log("fn3");
}

// Test

import * as Page from "xxx";

it("test async", () => {
    // stub this async to isolate the SUT
    sinon.stub(Page, "fn2").resolves();
    const stub = sinon.stub(Page, "fn3");

    Page.fn1();

    sinon.assert.calledOnce(stub);
});


/*

    AssertError: expected fn3 to be called once but was called 0 times

      276 |         Page.fn1();
      277 |
    > 278 |         sinon.assert.calledOnce(stub);
          |                      ^
      279 |     });
      280 | });

      at Object.fail (node_modules/sinon/lib/sinon/assert.js:107:21)
      at failAssertion (node_modules/sinon/lib/sinon/assert.js:66:16)
      at Object.calledOnce (node_modules/sinon/lib/sinon/assert.js:92:13)
      at Object.<anonymous> (src/test.test.tsx:278:22)
*/

Upvotes: 1

Views: 3889

Answers (1)

zetavg
zetavg

Reputation: 318

As @jonrsharpe suggested, your sinon.assert.calledOnce(stub); will be called before the fn2 promise inside Page.fn1 is resolved (i.e. .then(() => { fn3(); }) is called), based of the JavaScript "single thread, event loop" nature. Here's an article for reference: https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke.

You can try changing your code to:

    ...

    return Page.fn1().then(() => {
      sinon.assert.calledOnce(stub);
    });
    
    ...

or

it("test async", async () => {
    // stub this async to isolate the SUT
    sinon.stub(Page, "fn2").resolves();
    const stub = sinon.stub(Page, "fn3");

    await Page.fn1();

    sinon.assert.calledOnce(stub);
});

so that sinon.assert.calledOnce(stub); will be called after Page.fn1() is resolved.

Upvotes: 1

Related Questions