如何使用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实现更强大的桩:
- 安装依赖:
npm install sinon --save-dev - 修改测试代码:
const sinon = require('sinon'); // ... beforeEach(function() { sinon.stub(User, 'findOne').rejects({ code: 500, message: "forced error" }); }); afterEach(function() { User.findOne.restore(); });
额外注意点
- 确保请求的
POST /api/getUser能正确把请求体的name挂载到req.name上(比如是否有对应的中间件处理)。 - 模拟错误时必须返回包含
code属性的对象,否则catch块里err.code会是undefined,导致HTTP状态码异常。
内容的提问来源于stack exchange,提问作者MarcoS
相关产品推荐
相关产品推荐

