使用Sinon Stub Firestore失效问题排查求助
问题:Sinon Stub Firestore Document Reference的get方法未生效
问题场景
Node.js Firebase项目,使用Mocha做测试框架,Firebase模拟器运行正常,但尝试用Sinon模拟指定Document Reference的get方法返回错误时,Stub完全不生效,云函数始终调用原始的Firestore get方法,导致测试失败。
测试代码中关键Stub逻辑:
beforeEach(async () => { sandbox = sinon.createSandbox(); // 尝试Stub指定文档的get方法 docStub = sandbox.stub(admin.firestore().collection(landingPagesPath).doc("3"), 'get'); });
云函数中调用get的核心逻辑:
const landingPageRef = admin.firestore() .collection(landingPagesPath).doc(id); const landingPage = await landingPageRef.get().catch((error) => { throw new functions.https.HttpsError( "aborted", "failed fetching landing page", error ); });
测试报错:
THE ERROR AssertionError: Expected an error but got success Promoter Functions Tests createPromoter Tests test should fail if fetching landingpage failed: AssertionError: expected AssertionError: Expected an error but got… { …(3) } to be an instance of HttpsError
核心原因
每次调用admin.firestore().collection(...).doc(id)都会生成全新的DocumentReference实例。你在测试代码里Stub的是测试环境中创建的那个实例的get方法,但云函数运行时会自行创建新的DocumentReference实例,两者不是同一个对象,所以Stub无法命中目标方法。
解决方案
方案1:Stub DocumentReference原型的get方法
直接Stub DocumentReference类原型上的get方法,通过判断文档ID来决定是否返回错误,其他情况保留原始逻辑:
beforeEach(async () => { sandbox = sinon.createSandbox(); // 获取DocumentReference的原型对象 const docRefPrototype = Object.getPrototypeOf(admin.firestore().collection('dummy').doc('dummy')); // 原型上的get方法,针对ID为"3"的文档返回错误 sandbox.stub(docRefPrototype, 'get').callsFake(async function() { // @ts-ignore 忽略类型检查,因为this指向当前DocumentReference实例 if (this.id === "3") { throw new Error('Failed to fetch landing page'); } // 其他文档调用原始的get方法 return (docRefPrototype.get as sinon.SinonStub).wrappedMethod.call(this); }); });
方案2:Stubdoc方法返回预定义Stub实例
通过Stubcollection().doc()方法,让它在匹配特定ID时返回带有Stubbed get方法的实例:
beforeEach(async () => { sandbox = sinon.createSandbox(); // Stub firestore的collection方法 const collectionStub = sandbox.stub(admin.firestore(), 'collection').callsFake((colPath) => { const originalCollection = admin.firestore().collection(colPath); // 再Stub该collection的doc方法 const docStub = sandbox.stub(originalCollection, 'doc').callsFake((docId) => { const originalDocRef = originalCollection.doc(docId); // 对ID为"3"的文档Stub get方法 if (docId === "3") { sandbox.stub(originalDocRef, 'get').rejects(new Error('Failed to fetch landing page')); } return originalDocRef; }); return originalCollection; }); });
关键注意事项
- 务必在
afterEach中调用sandbox.restore(),避免Stub污染其他测试用例 - 如果需要模拟多个文档的错误,只需扩展方案中的ID判断逻辑即可
- 方案1会影响所有DocumentReference实例,需确保其他测试用例不受影响,或在测试后彻底清理Stub
内容的提问来源于stack exchange,提问作者Marcel
相关产品推荐
相关产品推荐

