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

使用Sinon存根MongoDB调用时遇「无法存根不存在的fetch属性」错误

无法存根仓库中的fetch函数,报错TypeError: Cannot stub non-existent property fetch

我在测试路由时,尝试存根仓库里调用数据库的fetch函数,但运行测试时报错TypeError: Cannot stub non-existent property fetch。相关代码如下:

测试文件

import { expect } from "chai";
import sinon from "sinon";
import request from "supertest";
import app from "../src/app.js";

describe("GET /users", () => {
  let fetchStub;

  beforeEach(() => {
    fetchStub = sinon.stub(
      import("../src/repository/product.repository.js"),
      "fetch"
    );
  });

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

  it("should return One Product", async () => {
    const product = {
      _id: new ObjectId(),
      code: 123456,
      url: "https://example.com",
      imported_t: "2020-02-07T16:00:00Z",
    };
    fetchStub.resolves(product);

    const res = await request(app)
      .get(`/products/findOne/${product.code}`)
      .set("x-api-key", "my-api-key");

    expect(res.status).to.equal(200);
  });
});

Repository代码

import db from "../db/db.js";

const collection = db.collection("products");

export const fetch = async (code) => {
  return await collection.findOne({ code: parseInt(code) });
};

Controller代码

import {
  fetch
} from "../repository/product.repository.js";


export const getProduct = async (req, res) => {
  try {
    const { code } = req.params;
    const product = await fetch(code);
    if (!product) {
      return res.status(404).json({ message: "Product not found" });
    }

    res.status(200).json(product);
  } catch (error) {
    console.error(error);
    res.status(400).json({ message: error.message });
  }
};

解决方法

问题核心是ES模块的导出为只读属性,无法直接通过sinon.stub修改导出对象的属性。以下是具体修复步骤:

1. 使用Sinon的stubModule方法(Sinon v15+)

ES模块环境下,需先导入模块,再用stubModule来存根导出的函数:

beforeEach(async () => {
  const productRepo = await import("../src/repository/product.repository.js");
  fetchStub = sinon.stubModule(productRepo, "fetch");
});

2. 补全测试文件的依赖缺失

测试中使用了ObjectId但未导入,需添加:

import { ObjectId } from "mongodb";

3. 修正测试描述的不一致性

测试针对/products/findOne接口,但describe标注的是GET /users,建议修改为:

describe("GET /products/findOne/:code", () => {
  // 测试逻辑
});

完整修改后的测试文件

import { expect } from "chai";
import sinon from "sinon";
import request from "supertest";
import { ObjectId } from "mongodb";
import app from "../src/app.js";

describe("GET /products/findOne/:code", () => {
  let fetchStub;

  beforeEach(async () => {
    const productRepo = await import("../src/repository/product.repository.js");
    fetchStub = sinon.stubModule(productRepo, "fetch");
  });

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

  it("should return One Product", async () => {
    const product = {
      _id: new ObjectId(),
      code: 123456,
      url: "https://example.com",
      imported_t: "2020-02-07T16:00:00Z",
    };
    fetchStub.resolves(product);

    const res = await request(app)
      .get(`/products/findOne/${product.code}`)
      .set("x-api-key", "my-api-key");

    expect(res.status).to.equal(200);
    expect(res.body).to.deep.equal(product);
  });
});

备选方案(Sinon版本低于v15)

如果无法升级Sinon,可通过代理模块实现存根:

  1. 创建代理模块src/repository/product.repository.proxy.js:
import * as productRepo from "./product.repository.js";
export default productRepo;
  1. 修改Controller的导入:
import productRepo from "../repository/product.repository.proxy.js";

export const getProduct = async (req, res) => {
  try {
    const { code } = req.params;
    const product = await productRepo.fetch(code);
    // 其余逻辑不变
  } catch (error) {
    // 错误处理不变
  }
};
  1. 测试文件中存根代理模块:
beforeEach(async () => {
  const productRepoProxy = await import("../src/repository/product.repository.proxy.js");
  fetchStub = sinon.stub(productRepoProxy.default, "fetch");
});

内容的提问来源于stack exchange,提问作者Ed Angelis Ribeiro

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:05:07