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

如何使用Chai测试Node.js+Mongoose接口的.catch()代码块?

测试Mongoose接口的catch代码块失败:模拟的findOne未生效

我想通过端到端测试实现代码库高覆盖率,所以需要测试接口里的.catch()代码逻辑。我的API基于Node.js+Mongoose开发,用Chai和Mocha做测试。

我写了如下代码:

src/controllers/user.controller.js

const User = require("../models/user.model");

const getUser = async(req, res) => {
  try {
    const user = await User.findOne({name: req.name});
    return res.status(200).json(user);
  } catch (err) {
    // 这是我要测试的代码
    console.error(`Error finding user ${req.name}:`, err);
    return res.status(err.code).json({ message: err });
  }
}

src/models/user.model.js

const mongoose = require("mongoose");

const UserSchema = mongoose.Schema({
  name: {
    type: String,
    required: "Name is required",
  },
});
module.exports = mongoose.model("User", UserSchema);

test/user.test.js

const chai = require("chai");
 const chaiHttp = require("chai-http");
 const spies = require("chai-spies");
 const User = require("../src/models/user.model");
 chai.use(chaiHttp);
 chai.use(spies);
 chai.should();

 describe("should handle mongoose errors", function() {
    describe("faulty User.findOne method", function() {
      const _User_findOne_Backup = User.findOne;
      beforeEach(function() {
        // 该函数应重载真实的findOne,但未生效!
        User.findOne = function() {
          return Promise.reject("forced error");
        };
      });
      afterEach(function() { // 测试后恢复真实函数
        User.findOne = _User_findOne_Backup; 
      }); 

      it("signup should respond with a server error", function() {
        const spy = chai.spy();
        return chai
          .request(server)
          .post("/api/getUser")
          .send({name: "Alice"})
          .then(spy)
          .catch((err) => {
            const res = err.response;
            res.should.have.status(500);
          })
          .then(() => {
            spy.should.not.have.been.called();
          })
        ;
      });
    });
  });

问题

测试里从来没调用我模拟的User.findOne()方法,实际跑的是Mongoose原生的findOne,导致getUser不会抛出错误,测试直接失败。我没找到问题出在哪,需要的话可以提供更多代码或上下文。


解决方法

Mongoose的模型方法(比如findOne)并非挂载在模型构造函数上的普通方法,而是存在于Mongoose的查询构造链或模型原型中,直接替换User.findOne不会生效。你需要用测试桩工具来拦截Mongoose的方法,以下是两种可行方案:

方案1:用Chai-spy的stub功能

修改测试中的beforeEach和afterEach,直接stub住User.findOne:

beforeEach(function() {
  // 模拟拒绝的Promise,同时返回包含code的错误对象
  chai.spy.on(User, 'findOne', () => Promise.reject({ code: 500, message: "forced error" }));
});

afterEach(function() {
  chai.spy.restore(User, 'findOne');
});

方案2:使用Sinon(更灵活的测试桩工具)

如果Chai-spy的能力不够,可以用Sinon实现更强大的桩:

  1. 安装依赖:npm install sinon --save-dev
  2. 修改测试代码:
const sinon = require('sinon');

// ...

beforeEach(function() {
  sinon.stub(User, 'findOne').rejects({ code: 500, message: "forced error" });
});

afterEach(function() {
  User.findOne.restore();
});

额外注意点

  1. 确保请求的POST /api/getUser能正确把请求体的name挂载到req.name上(比如是否有对应的中间件处理)。
  2. 模拟错误时必须返回包含code属性的对象,否则catch块里err.code会是undefined,导致HTTP状态码异常。

内容的提问来源于stack exchange,提问作者MarcoS

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 01:02:42