Flame_Phoenix
Flame_Phoenix

Reputation: 17564

Sinon.spy not working on simple object

Backgoround

I am trying to test a function called multiQuery and to make sure that it calls another function query with the correct parameters and the correct number of times.

Code

Following is a simplification of the code I am using. This is the object I use to create the object:

const createDb = () => {

    const query = (sql, values, type = "all") => {
        console.log(`original query called: ${sql}, ${values}, ${type}`);
        return Promise.resolve();
    };

    const multiQuery = requests => Promise.all(
        requests.map( req => query(req.sql, req.values, req.mode || "all") )
    );

    return {
        query,
        multiQuery
    };
};

And here is the test:

const sinon = require("sinon");
const chai = require("chai");
const expect = chai.expect;
const sinonChai = require("sinon-chai");
chai.use(sinonChai);

describe("multiQuery", () => {

        it("should call query multiple times with the correct parameters", async() => {
            const requests = [
                { sql: "hello", values: [1, 2, 3],  mode: "first"   },
                { sql: "world", values: [4, 5, 6],  mode: "all"     },
                { sql: "moon",  values: [7, 8]  }
            ];

            const db = createDb();
            const spy = sinon.spy( db, "query" );

            await db.multiQuery( requests );

            expect( spy ).to.have.been.calledThrice();
            expect( spy ).to.have.been.calledWith( "hello", [1, 2, 3], "first" );
        });
});

Problem

Problem is that no matter what I do, I always get the message:

AssertionError: expected query to have been called exactly thrice, but it was called 0 times

And I can't get to fix it.

This happens because multiQuery is bound to the original function instead of the spy.

Brainstorming

I was thinking of perhaps injecting the query dependency in multiQuery, but then I don't want to be passing a dependency every time I call it, nor do I want to pollute the object's API with unneeded factory methods.

I really have no idea on how to solve this here.... How can I fix this?

Upvotes: 1

Views: 596

Answers (2)

Tristan Hessell
Tristan Hessell

Reputation: 950

You are correct as to why the test is failing.

You need to make multiQuery reference the same query as what is returned, by putting query on an object:


A potential solution:

const createDb = () => {
   const db = { //the name here isn't important
        query,
        multiQuery
    };

    const query = (sql, values, type = "all") => {
        console.log(`original query called: ${sql}, ${values}, ${type}`);
        return Promise.resolve();
    };

    const multiQuery = requests => Promise.all(
        requests.map( req => db.query(req.sql, req.values, req.mode || "all") )
    );

    return db;
};

Upvotes: 1

Flame_Phoenix
Flame_Phoenix

Reputation: 17564

After asking around and making more tests, I have found that if I change the createDb function to the following:

const createDb = () => {
    const db = {}
    db.query = (sql, values, type = "all") => {
        console.log(`original query called: ${sql}, ${values}, ${type}`);
        return Promise.resolve();
    };
    db.multiQuery = requests => Promise.all(
        requests.map( req => db.query(req.sql, req.values, req.mode || "all") )
    );

    return db;
}

The spy method in sinon will work as expected.

Upvotes: 1

Related Questions