Sinon spy返回undefined且res.send未调用致单元测试失败
问题根因
测试失败是三个问题叠加导致的:
- stub目标完全错误:业务代码实际调用的是你自定义的
UserCollection模型的find()方法,但测试中替换的是mongoose.Model基类的find方法。Mongoose自定义模型在创建时就会绑定自身的模型方法,修改基类不会影响已经初始化完成的UserCollection,所以测试运行时UserCollection.find()根本没有走你预设的stub逻辑,要么发起真实的数据库查询(测试环境通常无可用库连接,直接抛错),要么返回的Promise状态完全不受控。 - res mock逻辑不符合调用链要求:业务代码的调用写法是
res.status(xxx).send(xxx),要求status()执行后的返回值必须挂载send方法。但你写的mock里,statusstub返回的是只有json属性的对象,上面根本没有send方法——就算find逻辑正常走到then分支,执行.send()时会直接抛出TypeError: res.status(...).send is not a function,错误进入catch分支后会触发完全一样的类型错误,导致send永远没有机会被成功调用。 - 异步等待未覆盖实际逻辑:因为stub失效+res mock报错,你写的
await findAllUsers()没有等到预期的响应逻辑执行完成就走到了断言步骤,自然统计到的res.send.callCount是0。
*额外提一句:你贴的业务代码本身有逻辑错误,查询成功的分支也设置了500状态码,正常成功响应应该返回200状态码。
修复步骤
- 测试文件中先引入实际业务调用的
UserCollection模型,直接stub这个模型上的find方法,不要修改mongoose基类。 - 修正
res的mock逻辑,让status()调用后返回res对象本身,保证链式调用能正常命中send的spy。 - 修正业务代码中成功分支的状态码错误。
修复后的业务代码
exports.findAllUsers = (req, res) => { UserCollection.find() .then(users => { res.status(200) // 修正原代码成功分支误写500的问题 .send(users) }) .catch(err => { res.status(500) .send({message: err.message || "error occurred retriving users informations"}) }) }
修复后的测试代码
// 先引入业务代码实际使用的UserCollection模型,路径替换为你项目中的实际路径 const UserCollection = require('../models/UserCollection'); describe("findAllUsers", () => { const sandbox = sinon.createSandbox(); afterEach(function () { sinon.restore(); sandbox.restore(); }); const req = { params: { id: new mongoose.Types.ObjectId(), }, }; // 修正res mock,保证链式调用正常 const res = { send: sinon.spy(), status: sinon.stub().callsFake(() => res) // status调用后返回res自身,链式调用可正常访问send }; it("should return all users data if found", async () => { // 直接stub实际调用的UserCollection.find,resolves是sinon封装的返回resolved Promise的简写 sandbox.stub(UserCollection, 'find').resolves("users data"); await findAllUsers(req, res); // 此时res.send.callCount会输出1 expect(res.status).to.have.been.calledWith(200); expect(res.send).to.have.been.calledWith("users data"); }); });
内容的提问来源于stack exchange,提问作者Aziz
相关产品推荐
相关产品推荐

