You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)无法访问。以下是修正思路和代码:

问题根源

  1. MongoClient.prototype.db()和collection()必须依赖实例化的MongoClient对象才能正常运行,直接通过原型调用会丢失上下文,触发属性访问错误。
  2. 你的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最贴近业务代码的依赖(比如直接mocksourceCollection的deleteMany),测试更稳定,也避免上下文缺失问题。
  • 确保mock函数返回Promise(用mockResolvedValue),因为deleteMany是异步方法,原代码用了await。
  • 测试中要验证mock方法是否被调用,确保逻辑走的是预期分支。

内容的提问来源于stack exchange,提问作者Cesar Augusto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 16:05:12