This is my controller function I want to test
const register = async (req, res) => {
const user = await User.create({
name: req.body.name,
username: req.body.username,
password: req.body.password,
email: req.body.email
}).catch(error => handleServerErrorResponse(res));
console.log('isUserRes?', user === res);
if(user) res.status(201).json(user.toJSON());
}
This is my handleServerErrorResponse
function
handleServerErrorResponse: (res, error = 'A server error occured') => {
return res.status(500).send({
message: error
});
},
This is my test implementation (I'm trying to mock mongoose model 'create' call and have it fail)
it('should fail', async () => {
// arrange
jest.spyOn(User, 'create').mockRejectedValue('error');
const req = mockRequest();
const res = mockResponse();
req.body = payload;
// act
await register(req, res);
// assert
expect(res.status).toHaveBeenCalledWith(500);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({
message: 'A server error occured'
})
);
})
Running the tests gives me the following
jest --silent=false --testEnvironment=node --runInBand --detectOpenHandles --coverage ./tests
console.log
isUserRes? true
● register › should fail
TypeError: user.toJSON is not a function
My question is why is const user
not undefined
when the create Promise fails? I've created a jsbin here that shows the value remains undefined when the await Promise fails. So what am I doing wrong here? And why is it referencing the response object?
Note: Even when sending real data using a HTTP client, if the create promise fails, the const user
still references the response object and becomes truthy, failing my check. I am aware that this can be fixed using a then
handler
.then(user => res.status(201).json(user));
but I am curious to know why is it behaving like this.
Update: Changed my handleServerErrorResponse
function to this
handleServerErrorResponse: (res, error = 'A server error occured') => {
res.status(500).send({
message: error
});
return;
},
As suggested by haggbart, this function was responsible for setting the user to the returned response object.
CodePudding user response:
I think the problem here is that you're catching the error in the register function and returning the result of calling handleServerErrorResponse, rather than just rethrowing the error. This means that the user variable will be assigned the result of calling handleServerErrorResponse, which is the response object.
Instead of catching the error in the register function, you could rethrow it and catch it in the test itself, like this:
const register = async (req, res) => {
const user = await User.create({
name: req.body.name,
username: req.body.username,
password: req.body.password,
email: req.body.email
});
console.log('isUserRes?', user === res);
if(user) res.status(201).json(user.toJSON());
}
it('should fail', async () => {
// arrange
jest.spyOn(User, 'create').mockRejectedValue('error');
const req = mockRequest();
const res = mockResponse();
req.body = payload;
// act
try {
await register(req, res);
} catch (error) {
// assert
expect(res.status).toHaveBeenCalledWith(500);
expect(res.send).toHaveBeenCalledWith(
expect.objectContaining({
message: 'A server error occured'
})
);
}
})
This way, the error will be thrown in the register function, and caught in the test, so the user variable will remain undefined.