I'm pretty new to using Sinon. I have the following test I've written, and it fails because res.status
always comes back as not called.
import chai from 'chai';
import 'chai/register-should';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import { db } from '../../models';
import * as loginController from '../../controllers/login';
chai.use(sinonChai);
describe('Login controller', () => {
describe('post function', () => {
let findOne, req, status, send, res;
beforeEach(() => {
findOne = sinon.stub(db.User, 'findOne');
findOne.resolves(null);
req = { body: { email: 'test@test.com', password: 'testpassword' }};
status = sinon.stub();
send = sinon.spy();
res = { send: send, status: status };
status.returns(res);
loginController.post(req, res);
});
afterEach(() => {
findOne.restore();
});
it('should return a 401 status for an invalid email', (done) => {
res.status.should.be.calledWith(401);
findOne.restore();
done();
});
});
});
The method in the controller right now is pretty simple. It uses a sequelize findOne
method first. If it doesn't find a matching email it should throw a 401. Here's what that looks like:
export function post(req,res) {
const email = req.body.email;
const password = req.body.password;
db.User.findOne({
where: {email: email}
}).then(user => {
if (user) {
// Other stuff happens here
} else {
res.status(401).send('That email address does not exist in our system.');
}
}).catch((error) => {
res.status(500).send(error.message);
});
}
When I run the test it does get to the else statement where it should be returning the status, but the test fails and when I check the log it says that the res.status
wasn't ever called.
The problem here is that the spec is synchronous and doesn't take a promise into account.
It makes sense to return a promise for testability reasons:
export function post(req,res) {
...
return db.User.findOne(...)
...
}
This can be naturally done if route handler is async
function.
Since Mocha supports promises, the specs can use async
functions instead of done
callback as well:
it('should return a 401 status for an invalid email', async () => {
const handlerResult = loginController.post(req, res);
expect(handlerResult).to.be.a('promise');
await handlerResult;
res.status.should.be.calledWith(401);
});