Jest测试MongoDB deleteMany未确认抛错场景 解决bsonOptions报错
问题
我有一个无参数方法executeBackup15d(),需要测试它执行失败的场景。该方法内部调用sourceCollection.deleteMany(),如果返回结果的acknowledged为false,就会抛出“Delete not acknowledged. Failed deletion”错误。我尝试用Jest的spyOn和mockImplementation重写方法来测试这个场景,但测试时出现TypeError: Cannot read properties of undefined (reading 'bsonOptions')错误,请问这个场景能不能测试?
方法executeBackup15d关键代码
const deleteResult = await sourceCollection.deleteMany(filter, getSessionObject()); if (deleteResult.acknowledged) logger_c.log('Documents deleted correctly'); else throw new Error('Delete not acknowledged. Failed deletion');
测试代码(仅展示it块)
it('Should throw a deletion error. Forced by mocking the mongoclient deletion method', async () => { // insert just one to avoid get not documents found error await collection.insertOne(generateFakeData(startDate, endDate)); const throwDeleteError = jest.fn(() => { { acknowledged: false } }); const originalDeleteMany = MongoClient.prototype.db().collection().deleteMany; jest.spyOn(MongoClient.prototype.db().collection(), 'deleteMany').mockImplementation(throwDeleteError); try { await executeBackup15d(); } catch (err) { expect(err.message).toBe('Delete not acknowledged. Failed deletion'); } // Restore the original deleteMany method jest.spyOn(MongoClient.prototype.db().collection(), 'deleteMany').mockImplementation(originalDeleteMany); });
错误日志
● fifteen › generalTest › Should throw a deletion error. Forced by mocking the mongoclient deletion method TypeError: Cannot read properties of undefined (reading 'bsonOptions') 104 | const throwDeleteError = jest.fn(() => { { acknowledged: false } }); 105 | > 106 | const originalDeleteMany = MongoClient.prototype.db().collection().deleteMany; | ^ 107 | 108 | jest.spyOn(MongoClient.prototype.db().collection(), 'deleteMany').mockImplementation(throwDeleteError); 109 |
解决方案
这个场景完全可以测试,报错是因为直接调用MongoClient.prototype.db().collection()时,缺失了MongoClient实例的必要上下文,导致内部属性(比如bsonOptions)无法访问。以下是修正思路和代码:
问题根源
MongoClient.prototype.db()和collection()必须依赖实例化的MongoClient对象才能正常运行,直接通过原型调用会丢失上下文,触发属性访问错误。- 你的mock函数写法有误:
() => { { acknowledged: false } }实际返回undefined,正确写法应为() => ({ acknowledged: false })(用括号包裹对象,避免被识别为代码块)。
修正后的测试代码(推荐方案)
直接mockexecuteBackup15d中使用的sourceCollection实例,避免从MongoClient原型层层mock:
it('Should throw a deletion error when deleteMany returns unacknowledged', async () => { // 插入测试数据 await collection.insertOne(generateFakeData(startDate, endDate)); // 定义正确的mock函数,返回异步的acknowledged=false结果 const mockDeleteMany = jest.fn().mockResolvedValue({ acknowledged: false }); // 假设sourceCollection是从某个模块导出的集合实例,直接spyOn该实例的deleteMany方法 const sourceCollection = require('../path/to/your/db-module').sourceCollection; const originalDeleteMany = sourceCollection.deleteMany; jest.spyOn(sourceCollection, 'deleteMany').mockImplementation(mockDeleteMany); try { await executeBackup15d(); // 如果未抛出错误,标记测试失败 expect(true).toBe(false); } catch (err) { expect(err.message).toBe('Delete not acknowledged. Failed deletion'); // 验证deleteMany是否被正确调用 expect(mockDeleteMany).toHaveBeenCalled(); } // 恢复原方法 sourceCollection.deleteMany.mockRestore(); });
替代方案:mock MongoClient返回链
如果必须从MongoClient原型入手,需要逐层mockdb()和collection()方法,确保上下文完整:
it('Should throw a deletion error when deleteMany returns unacknowledged', async () => { await collection.insertOne(generateFakeData(startDate, endDate)); const mockDeleteMany = jest.fn().mockResolvedValue({ acknowledged: false }); const mockCollection = jest.fn().mockReturnValue({ deleteMany: mockDeleteMany }); const mockDb = jest.fn().mockReturnValue({ collection: mockCollection }); // 替换MongoClient原型的db方法 const originalDb = MongoClient.prototype.db; jest.spyOn(MongoClient.prototype, 'db').mockImplementation(mockDb); try { await executeBackup15d(); expect(true).toBe(false); } catch (err) { expect(err.message).toBe('Delete not acknowledged. Failed deletion'); expect(mockDeleteMany).toHaveBeenCalled(); } // 恢复原方法 MongoClient.prototype.db.mockRestore(); });
关键注意事项
- 优先mock最贴近业务代码的依赖(比如直接mock
sourceCollection的deleteMany),测试更稳定,也避免上下文缺失问题。 - 确保mock函数返回Promise(用
mockResolvedValue),因为deleteMany是异步方法,原代码用了await。 - 测试中要验证mock方法是否被调用,确保逻辑走的是预期分支。
内容的提问来源于stack exchange,提问作者Cesar Augusto
相关产品推荐
相关产品推荐

