shadow
shadow

Reputation: 878

how to stub callback result in sinon?

I am new to nodejs.

How do I stub my return result which is a callback.

I know that I should not access the DB when doing testing.

I am doing unit testing at the controller level.

Here is my flow on how I going to do my test based on my understanding from java.

  1. Mock Request and Response.
  2. Set param of request.
  3. Mock bookDAO.selectBook so that it return a user defined result. Thus not calling DB.
  4. Verify / assert the value of the return results. (i.e. Response must be 200, JSON format, must have column BOOK_ID, BOOK_TITLE, etc)

However, i was not able to successfully mock my function. After running npm test, this is the error that I am receiving.

2018-10-02T10:00:17.809   1) Book service
       1. should list a SINGLE Book /book/id GET:
     Error: selectBook cannot yield to '[object Object]' since no callback was passed. Received [XCV1234, function (result) {

                res.status(200).json({
                        message: format(message.DEFAULT_MSG, "GET", constant.MODULE_URL),
                        result: result
                });
        }]
      at throwYieldError (node_modules\sinon\lib\sinon\call.js:22:11)
      at Object.yieldToOn (node_modules\sinon\lib\sinon\call.js:167:13)
      at Object.yieldTo (node_modules\sinon\lib\sinon\call.js:156:31)
      at Function.spyApi.(anonymous function) [as yieldTo] (node_modules\sinon\lib\sinon\spy.js:416:61)
      at Context.it (test\controller\BookController.spec.js:47:17)

Am i doing it the right way? how do i return the callback result ?


bookController.js:

exports.getBook = (req, res) => {   

    //get from request
    const id = req.params.id;

    const params = [id];

    bookDao.selectBook(params, function (result) {

        res.status(200).json({
            message: format(message.DEFAULT_MSG, "GET", constant.MODULE_URL),
            result: result  
        });
    });
};

bookDao.js:

function selectBook(params, callback) {

    pool.open(connString, function (err, conn) {

        conn.queryResult(query.SQL_SELECT, params, function (err, result) {

            if (err) {
                console.error(err);
                return conn.closeSync();
            }

            var data = result.fetchAllSync();

            // only when successful then call closeSync
            result.closeSync(); 

            return callback(data);
        });

        conn.close();
    });
}

bookRest.js:

module.exports = (app) => {

    // map to controller
    const controller = require('../controller/bookController');

    app.route(constant.MODULE_URL + '/:id').get(controller.getbook);

    app.route(constant.MODULE_URL).put(controller.updateBooks);
};

bookController.spec.js:

process.env.NODE_ENV = 'test';

const sinon = require('sinon');
const chai = require('chai');
const chaiHttp = require('chai-http');
const should = chai.should();
const httpMocks = require('node-mocks-http');

let server = require('../../../main.js');

const bookController = require('../../../controller/bookController.js');
const bookDao = require('../../../dao/bookDao.js');

chai.use(chaiHttp);

let req = httpMocks.createRequest();
let res = httpMocks.createResponse();

describe('Book service', () => {    
    beforeEach(() => {
    });

    afterEach(() => {
    });

    it('1. should list a SINGLE Book /book/id GET', (done) => {

        req.params.id = "XCV1234";
        const selectbook = sinon.stub(bookDao, "selectbook");

        bookController.getbook(req, res);
        selectbook.yieldTo({BOOK_ID : "XCV1234"});

        res.should.have.status(200);
        res.should.be.json;
        res.body.should.be.a('object');

        res.body.result[0].should.include.keys(
            'BOOK_ID'
        );

        sinon.restore();
        done();
     });    
});

Upvotes: 0

Views: 4742

Answers (1)

deerawan
deerawan

Reputation: 8443

I'm afraid yieldsTo is not the appropriate method to use for this case. Based on documentation, this method is intended to target callback that passed as property as in

sinon.stub(jQuery, "ajax").yieldsTo("success", [1, 2, 3]);

jQuery.ajax({
  success: function (data) {
    assertEquals([1, 2, 3], data);
  }
});

To solve your problem, we can use yields so it will be like:

...

// should be stubbed before `getbook` is called
sinon.stub(bookDao, "selectbook").yields({
  BOOK_ID: "XCV1234"
});

bookController.getbook(req, res);

res.should.have.status(200);

...

Hope it helps

Upvotes: 1

Related Questions