使用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,可通过代理模块实现存根:
- 创建代理模块
src/repository/product.repository.proxy.js:
import * as productRepo from "./product.repository.js"; export default productRepo;
- 修改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) { // 错误处理不变 } };
- 测试文件中存根代理模块:
beforeEach(async () => { const productRepoProxy = await import("../src/repository/product.repository.proxy.js"); fetchStub = sinon.stub(productRepoProxy.default, "fetch"); });
内容的提问来源于stack exchange,提问作者Ed Angelis Ribeiro
相关产品推荐
相关产品推荐

