Jest中mockResolvedValueOnce不生效,如何正确模拟insertOne?
解决Jest模拟MongoDB insertOne时的insertedId解构错误
问题描述
我实现了一个可正常运行的MongoDB数据插入函数insertOne:
export const insertOne = async (table, data) => { try { const client = await getClient(); const db = client.db(process.env.MONGO_DATABASE); const collection = db.collection(table); const { insertedId } = await collection.insertOne({ ...data, createdAt: moment().tz("America/Mexico_City").toISOString(), updatedAt: moment().tz("America/Mexico_City").toISOString(), }); const insertedData = await collection.findOne({ _id: insertedId }); return insertedData; } catch (error) { mwLogger('error', 'Error inserting one to the database :', { name: error?.name || 'Unknown Error', message: error?.message || 'No message provided', code: error?.code || 'No error code', stack: error?.stack || 'No stack trace available' }); throw new Error('Database insertion failed'); } }
但用Jest编写单元测试时,mockCollection.insertOne.mockResolvedValueOnce({ insertedId })无法正确模拟返回值,报错TypeError: Cannot read properties of undefined (reading 'insertedId')。测试代码如下:
describe('insertOne', () => { beforeEach(() => { // Mock db and collection setup mockDb = { collection: jest.fn() }; mockCollection = { insertOne: jest.fn(), findOne: jest.fn() }; mockClient = { db: jest.fn().mockReturnValue(mockDb) }; // Mock MongoClient.connect to return mockClient MongoClient.connect.mockResolvedValue(mockClient); // Mock collection method to return mockCollection mockDb.collection.mockReturnValue(mockCollection); }); it('should insert data correctly and return the inserted document', async () => { const mockData = { name: 'John Doe' }; // Mock the insertOne method to resolve with insertedId const insertedId = '123'; mockCollection.insertOne.mockResolvedValueOnce({ insertedId }); // Mock findOne to return the inserted data with timestamps const mockInsertedData = { _id: insertedId, ...mockData, createdAt: expect.any(String), updatedAt: expect.any(String), }; mockCollection.findOne.mockResolvedValueOnce(mockInsertedData); const result = await insertOne('users', mockData); // Assertions expect(mockCollection.insertOne).toHaveBeenCalledWith( expect.objectContaining({ ...mockData, createdAt: expect.any(String), updatedAt: expect.any(String), }) ); expect(result).toEqual(mockInsertedData); }); });
解决方案
报错的核心原因是未正确mock getClient函数:原函数通过getClient()获取数据库客户端,但测试仅mock了MongoClient.connect,若getClient是封装MongoClient.connect的自定义函数,测试框架无法自动关联该mock,导致getClient()返回undefined,后续client.db()调用失败,最终collection.insertOne()变成调用undefined的方法,返回值为undefined,解构insertedId自然报错。
具体修正步骤
Mock
getClient函数
根据getClient的导出方式选择对应mock方式:- 若
getClient与insertOne同文件:// 测试文件顶部引入模块 import * as dbModule from './你的数据库模块路径'; // 在beforeEach中mock getClient jest.spyOn(dbModule, 'getClient').mockResolvedValue(mockClient); - 若
getClient是单独模块:// 测试文件顶部直接mock整个模块 jest.mock('./getClient模块路径', () => ({ getClient: jest.fn().mockResolvedValue(mockClient) }));
- 若
完善beforeEach逻辑
将mockgetClient的代码加入beforeEach,确保每个测试用例都能获取正确的mock客户端,同时补充环境变量mock:beforeEach(() => { // 初始化mock对象 mockDb = { collection: jest.fn() }; mockCollection = { insertOne: jest.fn(), findOne: jest.fn() }; mockClient = { db: jest.fn().mockReturnValue(mockDb) }; // 关键:mock getClient返回预设的mockClient jest.spyOn(dbModule, 'getClient').mockResolvedValue(mockClient); // Mock数据库环境变量 process.env.MONGO_DATABASE = 'test-db'; // 保持collection的mock逻辑 mockDb.collection.mockReturnValue(mockCollection); });添加用例清理逻辑
在afterEach中清除mock和环境变量,避免测试用例间相互污染:afterEach(() => { jest.restoreAllMocks(); delete process.env.MONGO_DATABASE; });
修正后的完整测试示例
import * as dbModule from './你的数据库模块路径'; import { insertOne } from './你的数据库模块路径'; describe('insertOne', () => { let mockDb; let mockCollection; let mockClient; beforeEach(() => { // 初始化mock对象 mockDb = { collection: jest.fn() }; mockCollection = { insertOne: jest.fn(), findOne: jest.fn() }; mockClient = { db: jest.fn().mockReturnValue(mockDb) }; // Mock getClient返回预设客户端 jest.spyOn(dbModule, 'getClient').mockResolvedValue(mockClient); // Mock环境变量 process.env.MONGO_DATABASE = 'test-db'; // Mock collection返回mockCollection mockDb.collection.mockReturnValue(mockCollection); }); afterEach(() => { // 清除mock,避免用例间污染 jest.restoreAllMocks(); delete process.env.MONGO_DATABASE; }); it('should insert data correctly and return the inserted document', async () => { const mockData = { name: 'John Doe' }; const insertedId = '123'; // Mock insertOne返回结果 mockCollection.insertOne.mockResolvedValueOnce({ insertedId }); // Mock findOne返回插入的数据 const mockInsertedData = { _id: insertedId, ...mockData, createdAt: expect.any(String), updatedAt: expect.any(String), }; mockCollection.findOne.mockResolvedValueOnce(mockInsertedData); const result = await insertOne('users', mockData); // 验证insertOne的调用参数 expect(mockCollection.insertOne).toHaveBeenCalledWith( expect.objectContaining({ ...mockData, createdAt: expect.any(String), updatedAt: expect.any(String), }) ); // 验证返回结果 expect(result).toEqual(mockInsertedData); // 验证getClient被调用 expect(dbModule.getClient).toHaveBeenCalled(); // 验证db方法被传入正确的数据库名 expect(mockClient.db).toHaveBeenCalledWith('test-db'); }); });
内容的提问来源于stack exchange,提问作者Carlos
相关产品推荐
相关产品推荐

