javascriptunit-testingtestingsinonava

Sinon unit testing. How to unit test a function that will return a promise that will call another function with callback?


I am new to Sinon, but I have looked around for a while trying to find an answer for this question..

I have a function I need to test, it returns a promise to call another function will callback.. Below is the function that I need to write test case for:

const bookService = require(./bookService);

const getBook = () => {
  const bookName = "book";
  const bookID = '111';
  return new Promise((resolve, reject) => {
    bookService.InfoRequest(bookName, bookID, 'GET', res => {
      if(res.error){
         reject(res);
      }else{
         const list = res['allPages'] || [];
         if(list = []){
           resolve({
             pageNumber: 0,
             note: "book is no longer exist"
           });
         }else{
             resolve(res['allPages']);
         }  
      }
    })
  })
} 

The bookService.InfoRequest method is not returning anything it returns the callback(res);

I have tried stub the bookService.InfoRequest method, but since it is not returning anything I am not sure how can I modified the callback parameter to test all 3 branchs..

I am using Ava, so I tried something like this:

test('getBook Error Block', t=> {
    const stub = sinon.stub(bookService, InfoRequest);
    stub.callsFake(() => {
    return { error: true };
    });
    
    return obj.getBook().then(res => {
    t.deepEqual(res, []);
}).catch(error => {
    console.log(error.error);
    t.deepEqual(error.error, true);
})

})

This is the test cases for the first Branch, the reject(res) branch. There are 2 more very similar only with different callFake.

But the problem is I am not able to print the error out and test shows it passed, but if I change true to false, it also pass...


Solution

  • The stubbed implementation by .callFake() is not correct. The bookService.InfoRequest() method accepts a callback parameter, the res is passed to this callback. So you need to provide a stubbed implementation with this callback function and pass your fake error.

    E.g.

    bookService.js:

    function InfoRequest(bookName, bookId, method, cb) {}
    
    module.exports = { InfoRequest };
    

    obj.js:

    const bookService = require('./bookService');
    
    const getBook = () => {
      const bookName = 'book';
      const bookID = '111';
      return new Promise((resolve, reject) => {
        bookService.InfoRequest(bookName, bookID, 'GET', (res) => {
          if (res.error) {
            reject(res);
          } else {
            const list = res['allPages'] || [];
            if ((list = [])) {
              resolve({
                pageNumber: 0,
                note: 'book is no longer exist',
              });
            } else {
              resolve(res['allPages']);
            }
          }
        });
      });
    };
    
    module.exports = { getBook };
    

    obj.test.js:

    const obj = require('./obj');
    const bookService = require('./bookService');
    const sinon = require('sinon');
    const test = require('ava');
    
    test('getBook Error Block', (t) => {
      const res = { error: new Error('network') };
      const stub = sinon.stub(bookService, 'InfoRequest').callsFake((bookName, bookId, method, callback) => {
        callback(res);
      });
    
      return obj.getBook().catch((res) => {
        t.deepEqual(res.error, res.error);
        sinon.assert.calledWith(stub, 'book', '111', 'GET', sinon.match.func);
      });
    });
    

    test result:

    > nyc ava --timeout=3000 "/Users/dulin/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/66702460/obj.test.js"
    
    
      1 test passed
    ----------------|---------|----------|---------|---------|-------------------
    File            | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------------|---------|----------|---------|---------|-------------------
    All files       |   71.43 |    16.67 |      75 |   71.43 |                   
     bookService.js |     100 |      100 |       0 |     100 |                   
     obj.js         |   69.23 |    16.67 |     100 |   69.23 | 11-18             
    ----------------|---------|----------|---------|---------|-------------------