AssertError: expeced to be called once but was called 0 times

Viewed 4337

I am using nodejs and sinon.

Current, when I run my application, i.e. UpdateTask class, it is working fine, even errors.

However when I start doing my unit testing, I faced the following issue.

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

I don't get it why it is called 0 times where it is supposed to once.

Is there anything wrong that I have done in my code?

UpdateTask Class:

function updateInfo() {

    let updateCountParams = [];
    let updateParams = [];

    let idsToUpdateList = null;

    tempTable.getBookForUpdateCount(updateCountParams, function (results) {

        if (results[0].RECCOUNT > 0) {

            tempTable.getBookForUpdate(updateParams, function (results) {

                idsToUpdateList = results;

                for (var i = 0; i < idsToUpdateList.length; i++) {
                    let id = idsToUpdateList[i].id;

                    let param = [];
                    param.push(id);

                    let request = api.sendRequest(id);

                    // Invoke asynchronous call
                    request
                        .buffer(true)
                        .end(function (err, res) {

                            if (err) {

                                tempTable.updateBook(param, function (updateBookResult) {

                                });

                                return console.error(err.status + " - " + err.message);
                            }

                            let data = {
                                body: res.body,
                                text: res.text
                            };

                            let bkData = data.text;

                            if (bkData == undefined || bkData == null) {

                                tempTable.updateBook(param, function (updateBookResult) {

                                });

                                return console.error("DATA NOT FOUND".red);
                            }

                            //success flow business logic here
                            ...


                        }); //end asynchronous call
                }
            });
        }
        else {
            //no record to be processed.
            return;
        }
    });
}

test case:

    describe('Update Task', () => { 
    beforeEach(() => {

    });

    afterEach(() => {
        sinon.restore();
    });


    it('3. API Call - Errror: 404 - Not found', (done) => {

        let getTempTableForUpdateCountSpy = sinon.stub(TempTableDao, "getBookForUpdateCount").yields(jsonResult.count.success.result);
        let getTempTableForUpdateSpy = sinon.stub(TempTableDao, "getBookForUpdate").yields(jsonResult.single.failure.result);
        let getTempTableUpdateSpy = sinon.stub(TempTableDao, "updateBook");

        let test = nock('https://test.api.com/id')
                .get('/ID125125/')
                .reply(404, {

                 });

        updateTask.updateInfo();

        sinon.assert.calledOnce(getTempTableForUpdateCountSpy);
        sinon.assert.calledOnce(getTempTableForUpdateSpy);
        test.interceptors[0].statusCode.should.be.equal(404);
        sinon.assert.calledOnce(getTempTableUpdateSpy);

        done();
    });
1 Answers

Issue

tempTable.updateBook is called during a callback that hasn't run by the time sinon.assert.calledOnce(getTempTableUpdateSpy); runs and fails.


Solution

Make sure that the callback that calls tempTable.updateBook has had a chance to run before asserting.

This is much easier when using Promises where the Promise can be returned and awaited in the test. This scenario is trickier since there are callbacks and no clean way to return something that can be awaited.

An important point to note is that the test will remain active until it times out or until done is called.

In this case it looks like updateBook is the last thing to happen in the code and the last thing that needs to be tested. For scenarios like this it is possible to provide a mock implementation for the stub and assert and then call done within the mock implementation.

Here is a simplified example:

import * as sinon from 'sinon';

const tempTable = {
  updateBook: () => {}
};

const updateInfo = () => {
  setTimeout(() => { tempTable.updateBook(); }, 0);  // simulate an asynchronous callback
}

test('updateInfo', (done) => {
  const spy = sinon.stub(tempTable, 'updateBook');
  spy.callsFake(() => {
    sinon.assert.calledOnce(spy);  // SUCCESS
    done();
  });
  updateInfo();
});

In your case you could do something like this:

it('3. API Call - Errror: 404 - Not found', (done) => {

  let getTempTableForUpdateCountSpy = sinon.stub(TempTableDao, "getBookForUpdateCount").yields(jsonResult.count.success.result);
  let getTempTableForUpdateSpy = sinon.stub(TempTableDao, "getBookForUpdate").yields(jsonResult.single.failure.result);
  let getTempTableUpdateSpy = sinon.stub(TempTableDao, "updateBook");

  let test = nock('https://test.api.com/id')
    .get('/ID125125/')
    .reply(404, {

    });

  getTempTableUpdateSpy.callsFake(() => {
    sinon.assert.calledOnce(getTempTableForUpdateCountSpy);
    sinon.assert.calledOnce(getTempTableForUpdateSpy);
    test.interceptors[0].statusCode.should.be.equal(404);
    sinon.assert.calledOnce(getTempTableUpdateSpy);
    done();
  });

  updateTask.updateInfo();
});
Related