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

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里,status stub返回的是只有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状态码。

修复步骤
  1. 测试文件中先引入实际业务调用的UserCollection模型,直接stub这个模型上的find方法,不要修改mongoose基类。
  2. 修正res的mock逻辑,让status()调用后返回res对象本身,保证链式调用能正常命中send的spy。
  3. 修正业务代码中成功分支的状态码错误。

修复后的业务代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:57:16