使用Jest模拟Firebase模块中deleteCollection函数失败排查
我看了你的代码,问题的核心在于你的触发器函数内部调用的是模块本地定义的deleteCollection,而不是你通过Jest Mock的导出版本,所以Jest根本没机会拦截到这个调用。
具体问题分析
在你的模块代码里,deleteProtocol直接调用的是同一个文件里的deleteCollection函数,而不是module.exports.deleteCollection。Jest的Mock只能覆盖模块导出的成员,无法直接替换模块内部的私有函数调用,这就是为什么你断言triggers.deleteCollection被调用时会失败——实际执行的是未被Mock的本地函数,和你Mock的导出函数完全是两个东西。
修复步骤
1. 调整模块内的函数引用,让触发器调用导出的版本
修改你的模块代码,把deleteProtocol里的deleteCollection调用改成引用导出的函数:
const deleteProtocol = () => functions.firestore .document('protocols/{protocolId}') .onDelete((event, context) => { // 改成调用module.exports里的deleteCollection,这样Mock才能生效 return module.exports.deleteCollection(admin.firestore(), `protocols/${event.id}/trails`, 1); });
2. (可选但更优雅)将deleteCollection抽成独立模块
如果后续还有其他地方要用到这个函数,建议把它放到单独的工具模块里,比如../utils/collection-helpers.js:
// ../utils/collection-helpers.js function deleteCollection(db, collectionPath, batchSize) { return deleteQueryBatch(db, db.collection(collectionPath).orderBy('__name__').limit(batchSize), batchSize, []); } module.exports = { deleteCollection };
然后在触发器模块里导入使用:
// 触发器模块 const { deleteCollection } = require('../utils/collection-helpers'); const deleteProtocol = () => functions.firestore .document('protocols/{protocolId}') .onDelete((event, context) => { return deleteCollection(admin.firestore(), `protocols/${event.id}/trails`, 1); });
这样测试时直接Mock这个独立模块即可,避免模块内部函数引用的问题:
jest.mock('../utils/collection-helpers', () => ({ deleteCollection: jest.fn(() => []) }));
3. 修正测试中的事件参数(次要但必要)
你的测试里传入的event是普通对象,但Firestore的onDelete触发器接收的是文档快照对象,建议用测试工具生成正确的快照:
const event = testEnv.firestore.makeDocumentSnapshot({}, 'protocols/0');
这样event.id才能正确获取到文档ID,避免路径错误。
验证修复
调整后重新运行测试,Jest就能正确拦截到deleteCollection的调用,你的断言expect(triggers.deleteCollection).toBeCalled()应该就能通过了。
内容的提问来源于stack exchange,提问作者Frode Sjovatsen

