I have a middleware function which checks a session token to see if the user is an admin user. The function does not return anything if all checks pass but simply calls next().
How can I wait for the internal asynchronous Promise (adminPromise) to resolve before making assertions on the next() callback which is a Sinon spy? The test will currently fail because the assertion in the test is made before the resolution of the promise in AdminMiddleware.prototype.run.
The function is:
AdminMiddleware.prototype.run = function (req, res, next) {
let token = req.header(sessionTokenHeader);
let adminPromise = new admin().send()
adminPromise.then(function (authResponse) {
let adminBoolean = JSON.parse(authResponse).payload.admin;
if (adminBoolean !== true) {
return new responseTypes().clientError(res, {
'user': 'validation.admin'
}, 403);
};
next();
});
};
And the test:
it('should call next once if admin', function (done) {
stub = sinon.stub(admin.prototype, 'send');
stub.resolves(JSON.stringify({success : true, payload : {admin : true}}));
let nextSpy = sinon.spy();
AdminMiddleware.prototype.run({header: function () {}}, {}, nextSpy);
expect(nextSpy.calledOnce).to.be.true;
done();
});
At the moment I am wrapping the expectation like below, which will cause the test to pass, but seems like a hack. Furthermore, if it were to fail it would cause an unhandled promise rejection error and timeout due to done() not being called.
it('should call next once if admin', function (done) {
stub = sinon.stub(admin.prototype, 'send');
stub.resolves(JSON.stringify({success : true, payload : {admin : true}}));
let nextSpy = sinon.spy();
AdminMiddleware.prototype.run({header: function () {}}, {}, nextSpy);
stub().then(function () {
expect(nextSpy.calledOnce).to.be.true;
done();
});
});
One solution is to use a stub instead of a spy. Although they are two different things, I don't see you losing any functionality in this case as you were using an anonymous spy.
AdminMiddleware.prototype.run = function (req, res, next) {
const adminPromise = Promise.resolve()
adminPromise.then(function (authResponse) {
next()
})
}
it('should call next once if admin', function (done) {
const nextSpy = sinon.stub()
nextSpy.callsFake(() => {
expect(nextSpy.called).to.be.true
done()
})
AdminMiddleware.prototype.run({header: function () {}}, {}, nextSpy);
});
Also, you could avoid using sinon altogether for this assertion and do something like:
it('should call next once if admin', function (done) {
let called
const nextSpy = function() {
called = true
expect(called).to.be.true
done()
}
AdminMiddleware.prototype.run({header: function () {}}, {}, nextSpy);
});
In these two examples you will still get a timeout error if nextSpy isn't called, can't think of any reasonable way around that. If the unhandled promise rejection was very important to avoid I suppose you could do something like this:
nextSpy.callsFake(() => {
try {
expect(nextSpy.called).to.be.false
done()
} catch (e) {
console.log(e)
}
})
It would still fail the test due to the timeout, however it would not throw an unhandled promise rejection error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With