Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sinon to unit test 'catch' statement

I've got a simple function such as;

module.exports = {
  fetchUser:function(myUserId) {
    return new Promise((resolve, reject) => {
      this.getUser(myUserId)
        .then(user => {
          // do logic // then return user

          return user;
        })
        .then(resolve)
        .catch(err => {
          // whoops there has been an error

          let error = { error: 'My Error' };

          reject(error);
        });
    });
  }
};

I want to unit test both the resolve and reject result.

A simple chai test would be;

var expect = require('chai').expect;
var user = require('./user');

describe('User module', function() {
  it('test fetchUser', function() {
    let _user = user.fetchUser('abc123');

    return _user
      .then(user => {
        expect(data).to.be.an('object');
      });
  });

Using sinon or another library, how can I for the fetchUser function to throw that reject error?

like image 537
sketchthat Avatar asked Nov 30 '16 06:11

sketchthat


2 Answers

With Mocha, Chai and Sinon it can be implemented with stubbed method getUser.

const User = require("./fetchUserModule");

describe('User module', () => {
    beforeEach(() => User.getUser = sinon.stub());

    afterEach(() => User.getUser.reset());

    it('returns user if `getUser` returns data', () => {
        const user = {name: 'John'};
        User.getUser.withArgs("abc123").returns(Promise.resolve(user));

        return User.fetchUser("abc123").then(result => {
            expect(result).to.equal(user)
        }).catch(error => {
            expect(error).to.be.undefined;
        })
    });

    it('throws error if `getUser` is rejected', () => {
        User.getUser.withArgs("abc123").returns(Promise.reject());

        return User.fetchUser("abc123").then(result => {
            expect(result).to.be.undefined;
        }).catch(err => {
            expect(err).to.eql({error: 'My Error'})
        })
    });
});
like image 102
Krzysztof Safjanowski Avatar answered Nov 15 '22 11:11

Krzysztof Safjanowski


Start with anything in your "logic" that can throw an error.

If not you would need to stub this.getUser to reject or throw an error instead of returning data. sinon-as-promised patches sinon.stub to include the .resolves and .rejects promise helpers.

const sinon  = require('sinon')
require('sinon-as-promised')

Setup the stub for the failure tests.

before(function(){
  sinon.stub(user, 'getUser').rejects(new Error('whatever'))
})

after(function(){
  user.getUser.restore()
})

Then either catch the .fetchUser error or use chai-as-promised for some sugar.

it('test fetchUser', function() {
  return user.fetchUser('abc123')
    .then(()=> expect.fail('fetchUser should be rejected'))
    .catch(err => {
      expect(err.message).to.eql('whatever')
    })
})

it('test fetchUser', function() {
  return expect(user.fetchUser('abc123')).to.be.rejectedWith(Error)
})

or async if you live in the new world

it('test fetchUser', async function() {
  try { 
    await user.fetchUser('abc123')
    expect.fail('fetchUser should be rejected'))
  } catch(err) {
    expect(err.message).to.eql('whatever')
  }
})

As a side note, you don't need to wrap something that already returns a promise in new Promise and be careful about losing error information when chaining multiple .catch handlers.

fetchUser: function (myUserId) {
  return this.getUser(myUserId)
    .then(user => {
      //logic
      return user
    })
    .catch(err => {
      let error = new Error('My Error')
      error.original = err
      reject(error)
    });
}
like image 21
Matt Avatar answered Nov 15 '22 11:11

Matt