Search code examples
mocha.jssinonknex.js

How to use sinon to mock a series of knex call


Given a myknex.js:

export default { return require('knex')({client:'mysql',connection:{//conn}})}

and I want to write a unit test for the following function:

async function get({ userId }) {
  return await myknex('users')
    .where({ id: userId })
    .returning('*');
}

The unit test looks like:

const sinon = require('sinon');
const sandbox = sinon.createSandbox();
const { myknex } = require('./myknex');

it('no record', async () => {
    sandbox
    .stub(myknex('users'), 'executeQuery').resolves([]);
    const result = await myrepo.get({ userId: 1 });
    const expectedResult = [];
    expect(result).to.deep.equal(expectedResult);
  });

I got an error message about:

TypeError: Cannot stub non-existent own property executeQuery

How can I mock the chained myknex calls?


Solution

  • Since you have myknex.js export a function of knex, we need to use proxyquire to mock this in test file.

    const chai = require('chai');
    const expect = chai.expect;
    const sinon = require('sinon');
    const proxyquire = require('proxyquire'); // include proxyquire
    
    const expectedResult = [];
    const knexQuery = {
      where: sinon.stub().returnsThis(), // because we want to call `returning` afterwards
      returning: sinon.stub().resolves(expectedResult) // resolve it as promise
    }
    const myknexStub = sinon.stub().returns(knexQuery);
    const myrepo = proxyquire('./src', { './myknex': myknexStub }); // your source file and stub myknex here
    
    describe(('Test My Module'), () => {
      it('no record', async () => {
        const result = await myrepo.get({ userId: 1 });
    
        // many options to unit test the function    
        expect(myknexStub.calledWith('users')).to.be.ok;
        expect(knexQuery.where.calledWith({ id: 1 })).to.be.ok;
        expect(knexQuery.returning.calledWith('*')).to.be.ok;
        expect(knexQuery.returning.calledAfter(knexQuery.where)).to.be.ok;
        expect(result).to.deep.equal(expectedResult);
      });
    });
    

    Hope it helps