如何用Jest编写Node.js控制器单元测试并Mock MongoDB find操作
解决Node.js用户控制器Jest单元测试状态码验证问题
问题背景
需要为Node.js用户控制器的users方法编写Jest单元测试,实现100%代码覆盖率。要求MockUser.find()方法,使其分别返回空数组、用户列表,同时验证响应的状态码(如404、200、500),但当前测试代码无法完成状态码的断言验证。
相关业务代码
app.js
const route = require("./routes/app-route"); app.use('/api', route);
app-route.js
router.get('/users', [authJwt.verifyToken], usersController.users);
user-controller.js
exports.users = (req, res) => { User.find({}) .then(users => { if (users.length === 0) { return res.status(404).send({ message: "Users Not found." }); } res.status(200).send({ message: "Users Found", status: "success", users }); }) .catch(err => { res.status(500).send({ message: err.message }); }); };
问题原因
当前测试中的mRes是空对象,没有模拟Express响应对象的status和send方法,无法捕获状态码和响应内容。同时users方法是基于Promise的异步逻辑,需要正确处理异步测试流程。
解决方案
1. Mock User模型
用Jest模拟User模块,避免操作真实数据库:
const User = require('../path/to/user-model'); // 替换为实际文件路径 jest.mock('../path/to/user-model'); // 自动Mock整个User模块
2. 模拟响应对象
为res创建Mock函数,其中status需要返回res本身以支持链式调用:
const mockRes = () => { const res = {}; res.status = jest.fn().mockReturnValue(res); res.send = jest.fn().mockReturnValue(res); return res; };
3. 分场景编写测试
场景1:返回空数组,验证404状态码
test("当用户列表为空时返回404", async () => { // 准备数据 const mReq = {}; const mRes = mockRes(); User.find.mockResolvedValue([]); // Mock返回空数组 // 执行测试方法 await UsersContoller.users(mReq, mRes); // 断言验证 expect(mRes.status).toHaveBeenCalledWith(404); expect(mRes.send).toHaveBeenCalledWith({ message: "Users Not found." }); });
场景2:返回用户列表,验证200状态码
test("当存在用户时返回200和用户列表", async () => { // 准备数据 const mReq = {}; const mRes = mockRes(); const mockUsers = [{ id: 1, name: "Test User" }]; User.find.mockResolvedValue(mockUsers); // Mock返回用户列表 // 执行测试方法 await UsersContoller.users(mReq, mRes); // 断言验证 expect(mRes.status).toHaveBeenCalledWith(200); expect(mRes.send).toHaveBeenCalledWith({ message: "Users Found", status: "success", users: mockUsers }); });
场景3:查询出错时返回500状态码
test("当查询出错时返回500和错误消息", async () => { // 准备数据 const mReq = {}; const mRes = mockRes(); const mockError = new Error("Database connection failed"); User.find.mockRejectedValue(mockError); // Mock抛出错误 // 执行测试方法 await UsersContoller.users(mReq, mRes); // 断言验证 expect(mRes.status).toHaveBeenCalledWith(500); expect(mRes.send).toHaveBeenCalledWith({ message: mockError.message }); });
关键说明
- 使用
mockResolvedValue和mockRejectedValue简化Promise的Mock逻辑,替代繁琐的mockImplementation - 异步测试需使用
async/await确保测试等待Promise执行完成 - 通过断言
mRes.status和mRes.send的调用参数,精准验证响应状态码和内容
内容的提问来源于stack exchange,提问作者Brij Patel
相关产品推荐
相关产品推荐

