You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 17:45:42