Jest中mockImplementationOnce无法捕获上层方法传参问题
问题根源
出现无法获取_id的问题,本质是混淆了mock函数的入参来源和外层业务方法的闭包作用域:
对照removerFromDb对database的实际调用逻辑:
removerFromDb = async (collectionName, _id) => { // 传入database的唯一实参,是内部封装的数据库操作回调 await database(async (db) => { const collection = db.collection(collectionName); await collection.deleteOne({ _id: ObjectId(_id) }); }); this.initializeData(collectionName); }
database被调用时,仅会接收到那个数据库操作回调作为入参。_id、collectionName是removerFromDb自身的入参,仅在内部闭包和回调中使用,从未作为参数传给database本身。
之前设置两次mockImplementationOnce的调用顺序是匹配业务执行流的,但在第一次mock的实现中直接引用_id时,该变量既不在mock函数的作用域内,也没有通过闭包链路传递进来,运行时自然为undefined。之前在mock内部手动声明const _id =1的临时方案,本质是手动补了缺失的变量声明,属于硬编码兜底,完全没有利用测试入参的传递链路。
合理实现方式
完全不需要硬编码参数,顺着原业务的执行链路模拟即可,参考如下实现:
const data = require('../data/testingData.json'); it('Should remove item from list', async () => { // 把测试入参统一定义在测试用例顶层,所有逻辑共享同一变量 const testCollectionName = ""; const testTargetId = 1; let dataCopy = [...data]; // 第一次调用database:对应删除记录的操作 database .mockImplementationOnce(async (dbCallback) => { // 构造和真实结构一致的假db对象,匹配原代码的链式调用 const fakeCollection = { deleteOne: async (query) => { // 直接读取业务代码真实传入的删除条件做过滤,无硬编码 dataCopy = dataCopy.filter(item => item._id !== query._id); } }; const fakeDb = { collection: () => fakeCollection }; // 执行业务传入的回调,传入假db,让原逻辑自然跑通 await dbCallback(fakeDb); }) // 第二次调用database:对应initializeData里的全量取数操作 .mockImplementationOnce(() => Promise.resolve(dataCopy)); await DataManager.removerFromDb(testCollectionName, testTargetId); expect(DataManager.getData().length).toEqual(5); });
方案优势
- 无硬编码:完全顺着原业务的参数传递链路模拟,过滤逻辑依赖业务代码真实传入的查询条件,和实际运行逻辑完全一致
- 作用域清晰:所有测试入参在测试用例顶层统一定义,不会出现变量未定义、跨层取值的问题,后续调整测试入参只需要修改一处
- 兼容性强:哪怕后续
removerFromDb调整了删除的查询条件(比如新增状态过滤、多字段匹配),只要deleteOne收到的查询对象结构不变,测试代码不需要修改
内容的提问来源于stack exchange,提问作者Ohad Yeahhh
相关产品推荐
相关产品推荐

