Reputation: 5411
I have the following controller
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import { UserModel, isPasswordAllowed } from '../../models/User';
const saltRounds = 10;
function userController() {
function add(req, res) {
try {
if (req.body.administrator) {
res.status(400).json({
error: {
message: 'Bad Request',
},
});
return;
}
if (!isPasswordAllowed(req.body.password)) {
res.status(400).json({
error: {
message: 'La contraseña no cumple con los requisitos minimos',
},
});
return;
}
bcrypt.hash(req.body.password, saltRounds, async (err, hash) => {
if (err) {
res.status(500).json({ error: { code: '500', message: err.errmsg } });
return;
}
const user = new UserModel();
user.email = req.body.email.toLowerCase();
user.password = hash;
await user
.save()
.then(() => {
const token = jwt.sign(
{
username: user.email,
userId: user.id,
},
process.env.JWT_KEY,
{
expiresIn: '7d',
},
);
res.status(200).json({
message: 'Usuario Creado',
token,
email: user.email,
});
})
.catch((error) => {
if (error.code === 11000) {
res.status(400).json({
error: { code: '500', message: 'El correo ya existe' },
});
} else {
console.log(error);
res.status(500).json({ error: { code: '500', message: error.message } });
}
});
});
} catch (error) {
res.status(503).json({ error });
}
}
return {
add,
};
}
export default userController();
As you can expect this controller works great, the user is created in the database, but I have the following test:
import UserController from './UserController';
import { connect, closeDatabase, clearDatabase } from '../../__test__/db-handler';
describe('test UserController', () => {
const res = {};
beforeEach(async () => {
await connect();
res.send = jest.fn().mockReturnValue(res);
res.status = jest.fn().mockReturnValue(res);
res.json = jest.fn().mockReturnValue(res);
});
afterEach(async () => {
await clearDatabase();
});
afterAll(async () => {
await closeDatabase();
});
test('should return the expect api method', () => {
const userControllerApi = {
add: expect.any(Function),
};
expect(UserController).toMatchObject(userControllerApi);
});
test('should return 400 error bad request is body contains administrator: true', async () => {
const req = {
body: {
administrator: true,
},
};
await UserController.add(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toHaveBeenCalledWith({
error: {
message: 'Bad Request',
},
});
});
test('should return 400 error bad request is password is not allow', async () => {
const req = {
body: {
password: '123456',
},
};
await UserController.add(req, res);
expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toHaveBeenCalledWith({
error: {
message: 'La contraseña no cumple con los requisitos minimos',
},
});
});
// this test is not passing
test('should create an user and return a token', async () => {
const req = {
body: {
email: '[email protected]',
password: 'Abc123456',
},
};
const expectObject = {
message: 'Usuario Creado',
email: '[email protected]',
};
await UserController.add(req, res);
jest.useFakeTimers();
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledTimes(1);
expect(res.json).toMatchObject(expectObject);
});
});
but the last test 'should create an user and return a token' never pass and I get the following:
● test UserController › should create an user and return a token
expect(jest.fn()).toHaveBeenCalledWith(...expected)
Expected: 200
Number of calls: 0
78 | jest.useFakeTimers();
79 |
> 80 | expect(res.status).toHaveBeenCalledWith(200);
| ^
81 | expect(res.json).toHaveBeenCalledTimes(1);
82 | expect(res.json).toMatchObject(expectObject);
83 | });
I also debbug this code in testing mode and as you can see in the following image, the code is enter in the res.status(200).json({ .... })
, so I don't understand what it is happening here.
Upvotes: 0
Views: 593
Reputation: 24565
The problem is that you're mixing callbacks with async/await, meaning that execution of add()
will be finished before the callback of bcrypt.hash
has been finished. This results in res.status
not to have been called yet in your test.
You can fix this by awaiting the bcrypt.hash
call (it supports returning a promise by default):
// await hashing function instead of using callback
const hash = await bcrypt.hash(req.body.password, saltRounds);
const user = new UserModel();
user.email = req.body.email.toLowerCase();
user.password = hash;
// rest of the code ...
Upvotes: 1