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

如何使用Sinon正确存根返回对象的floatAPIModels?

解决Sinon存根floatAPIModels时的"Cannot stub non-existent property Person"错误

问题场景

测试insertFloatData函数时,尝试用Sinon存根floatAPIModels的Person属性,抛出错误:

TypeError: Cannot stub non-existent property Person

涉及的核心代码:

  • 待测试函数insertFloatData:
const floatAPIModels = require("models/float/floatAPIModels");

const insertFloatData = async (databaseConnection, endpoint, endpointData) => {
  try {
    const floatModel = floatAPIModels(databaseConnection);
    await databaseConnection.sync();

    if (endpoint === "people") {
      endpointData.forEach(async (record) => {
        await floatModel.Person.upsert(record);
      });
    }

    return true;
  } catch (error) {
    console.log("Unable to insert data into the database:", error);
    return error;
  }
};
  • 依赖的floatAPIModels是一个返回包含Person对象的函数:
const { DataTypes } = require("sequelize");

const floatAPIModels = (sequelize) => {
  const Person = sequelize.define(
    "Person",
    {
      people_id: { type: DataTypes.INTEGER, primaryKey: true },
      job_title: { type: DataTypes.STRING(200), allowNull: true },
      employee_type: { type: DataTypes.BOOLEAN, allowNull: true },
      active: { type: DataTypes.BOOLEAN, allowNull: true },
      start_date: { type: DataTypes.DATE, allowNull: true },
      end_date: { type: DataTypes.DATE, allowNull: true },
      department_name: { type: DataTypes.STRING, allowNull: true },
      default_hourly_rate: { type: DataTypes.FLOAT, allowNull: true },
      created: { type: DataTypes.DATE, allowNull: true },
      modified: { type: DataTypes.DATE, allowNull: true },
    },
    {
      timestamps: true,
      tableName: "Person",
    }
  );

  return { Person };
};

module.exports = floatAPIModels;

错误原因

floatAPIModels本身是一个函数,而非包含Person属性的对象。你试图存根的floatAPIModels.Person根本不存在,Person是这个函数执行后返回的对象的属性,所以Sinon会抛出"无法存根不存在的属性"的错误。

正确的测试代码

需要直接存根整个floatAPIModels函数,让它在被调用时返回我们预定义好的模拟对象:

const { expect } = require("chai");
const sinon = require("sinon");
const floatAPIModels = require("src/models/float/floatAPIModels");
const floatService = require("src/services/float/floatService");

describe("insertFloatData", () => {
  let databaseConnection;
  let floatModelMock;
  let floatAPIModelsStub;

  beforeEach(() => {
    // 模拟数据库连接,包含sync方法的存根
    databaseConnection = {
      sync: sinon.stub().resolves()
    };

    // 模拟Person模型及其upsert方法
    floatModelMock = {
      Person: { upsert: sinon.stub().resolves() },
    };

    // 存根整个floatAPIModels函数,让它返回我们的模拟对象
    floatAPIModelsStub = sinon.stub(floatAPIModels).returns(floatModelMock);
  });

  afterEach(() => {
    sinon.restore();
  });

  it("should insert endpointData into the 'people' endpoint", async () => {
    const endpoint = "people";
    const endpointData = [{ record: "data" }];

    await floatService.insertFloatData(databaseConnection, endpoint, endpointData);

    // 验证floatAPIModels被调用,且传入了数据库连接
    expect(floatAPIModelsStub.calledOnceWith(databaseConnection)).to.be.true;
    // 验证upsert被调用一次,且传入了正确的参数
    expect(floatModelMock.Person.upsert.calledOnceWith(endpointData[0])).to.be.true;
  });
});

额外注意点

原insertFloatData函数中存在一个潜在问题:使用forEach遍历endpointData并执行异步的upsert,但forEach不会等待所有异步操作完成。这会导致函数提前返回true,但实际可能还有upsert未执行完毕,测试也可能出现不稳定的情况。

建议将forEach改为for...of或者Promise.all:

// 方式1:用for...of(串行执行)
if (endpoint === "people") {
  for (const record of endpointData) {
    await floatModel.Person.upsert(record);
  }
}

// 方式2:用Promise.all(并行执行,效率更高)
if (endpoint === "people") {
  await Promise.all(endpointData.map(record => floatModel.Person.upsert(record)));
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 03:45:03