Sinon to unit test 'catch' statement
Solution 1
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'})
})
});
});
Solution 2
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)
});
}
Related videos on Youtube
sketchthat
Updated on June 04, 2022Comments
-
sketchthat almost 2 years
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
andreject
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 thefetchUser
function to throw thatreject
error? -
sketchthat over 7 yearsThanks. If I setup the test this way it doesn't actually ever enter my catch codeblock does it? The stub just fakes the response to match the error thrown from my catch.
-
sketchthat over 7 yearsThanks Matt. I didn't realise that last point, I will update my codebase.
-
Krzysztof Safjanowski over 7 yearsYes, it enters to
catch
statement as error is equal to{error: 'My Error'}
. What you need more to proof that it works? -
sketchthat over 7 yearsThanks, I was just checking, I don't fully understand stubs yet. I'll test it out.
-
Krzysztof Safjanowski over 7 yearsI tested it before post answer - it works as expected …