4

Trying to stub a chained knex query using Sinon. The query looks like the following

const result = await TableModel
  .query()
  .whereIn('id', idList)
  .whereIn('value', valueList);

Normally I use a helper function that I created that returns an instance of the model with each method stubbed to return this, like so

for (const method of ['query', 'whereIn']) {
  TableModel[method] = sandbox.stub().returnsThis();
}

Then stubbing the instance within the test to resolve the necessary test case

TableModel.whereIn = sandbox.stub().resolves({ object: 'stuff' });

However, this doesn't work when chaining the same method I'm getting an error from mocha / chai / sinon that reads

TypeError: TableModel.query(...).whereIn(...).whereIn is not a function

Looking for help on how to stub and resolve the method within the test.

Lin Du
  • 88,126
  • 95
  • 281
  • 483
parrott-kevin
  • 118
  • 1
  • 7

2 Answers2

0

I've been trying to stub similar scenario:

await model.query().findById();

and I was able to stub this in following way:

const stubbedData = { ... }; // Whatever you want to get

sandbox.stub(Model, 'query').returns({
  findById: sandbox.stub().returns(stubbedData),
});

In your case it will be very similar and if you need to distinguish between two whereIn then you can use withArgs or first whereIn could return another "nested" stub.

Here is a good article about stubbing complex objects:

https://codeutopia.net/blog/2016/05/23/sinon-js-quick-tip-how-to-stubmock-complex-objects-such-as-dom-objects/

iaforek
  • 2,860
  • 5
  • 40
  • 56
0

Here is the unit test solution, you can use stub.withArgs(arg1[, arg2, ...]); solve this.

tableModel.js:

const TableModel = {
  query() {
    return this;
  },
  async whereIn(field, values) {
    return "real data";
  },
};

module.exports = TableModel;

main.js:

const TableModel = require("./tableModel");

async function main(idList, valueList) {
  const result = await TableModel.query()
    .whereIn("id", idList)
    .whereIn("value", valueList);

  return result;
}

module.exports = main;

main.test.js:

const main = require("./main");
const TableModel = require("./tableModel");
const sinon = require("sinon");
const { expect } = require("chai");

describe("50957424", () => {
  afterEach(() => {
    sinon.restore();
  });
  it("should pass", async () => {
    sinon.stub(TableModel);
    TableModel.query.returnsThis();
    TableModel.whereIn.withArgs("id").returnsThis();
    TableModel.whereIn.withArgs("value").resolves({ object: "stuff" });
    const actual = await main([1, 2], ["a", "b"]);
    expect(actual).to.be.eql({ object: "stuff" });
    sinon.assert.calledOnce(TableModel.query);
    sinon.assert.calledWith(TableModel.whereIn.firstCall, "id", [1, 2]);
    sinon.assert.calledWith(TableModel.whereIn.secondCall, "value", ["a", "b"]);
  });
});

Unit test result with coverage report:

  50957424
    ✓ should pass


  1 passing (16ms)

---------------|----------|----------|----------|----------|-------------------|
File           |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files      |       92 |      100 |    66.67 |       92 |                   |
 main.js       |      100 |      100 |      100 |      100 |                   |
 main.test.js  |      100 |      100 |      100 |      100 |                   |
 tableModel.js |       50 |      100 |        0 |       50 |               3,6 |
---------------|----------|----------|----------|----------|-------------------|

Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/50957424

Lin Du
  • 88,126
  • 95
  • 281
  • 483