Jest中createQueryBuilder链式mockReturnValueOnce失效问题排查
问题原因分析
你遇到的问题核心在于:每次调用createQueryBuilderMock时,都会生成一个全新的getManyAndCount mock函数实例。
原来的代码中,createQueryBuilderMock是一个工厂函数,每次被调用(也就是你的service里调用repository.createQueryBuilder()时),都会返回一个包含新getManyAndCount mock的对象。第一个测试用例消耗了该mock的第一个mockReturnValueOnce,但第二个测试用例调用createQueryBuilder()时,得到的是另一个全新的getManyAndCount mock,它的返回值队列是从头开始的,所以依然会返回[myDataAllMock, 2]。
解决方案
推荐采用每个测试用例独立控制mock返回值的方式,既避免跨测试污染,也更灵活。具体步骤如下:
- 在
beforeEach中动态创建mock,将getManyAndCount的mock变量暴露出来,方便测试用例修改 - 每个测试用例单独设置
getManyAndCount的返回值
修改后的代码示例:
// 声明变量用于存储getManyAndCount的mock实例 let getManyAndCountMock: jest.Mock; let service: MyService; beforeEach(async () => { // 每次测试前重新创建getManyAndCount mock getManyAndCountMock = jest.fn(); const createQueryBuilderMock = jest.fn(() => ({ leftJoin: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), getManyAndCount: getManyAndCountMock // 复用同一个mock实例 })); const module: TestingModule = await Test.createTestingModule({ providers: [ MyService, { provide: MY_REPOSITORY, useValue: { createQueryBuilder: createQueryBuilderMock, } } ], }).compile(); service = module.get<MyService>(MyService); }); describe('getAll', () => { it('should return all data', async () => { // 为当前测试设置返回值 getManyAndCountMock.mockReturnValue([myDataAllMock, 2]); expect(await service.getAll({ perPage: 250, page: 1 } as GetMyDataDTO)).toEqual({ meta: {...metaMock, totalItems: 2}, myData: mappedMyDataMock, }); }); it('should return filtered by orderNumber data', async () => { // 为当前测试设置返回值 getManyAndCountMock.mockReturnValue([myDataAllMock[1], 1]); expect(await service.getAll({ perPage: 250, page: 1, orderNumber: '12553624' } as GetMyDataDTO)).toEqual({ meta: metaMock, myData: [mappedMyDataMock[1]], }); }); });
备选方案(复用mock队列)
如果你坚持想用mockReturnValueOnce的链式调用,可以把getManyAndCount的mock提取出来,让所有createQueryBuilder返回的对象复用同一个mock实例:
// 提前创建getManyAndCount的mock,设置返回值队列 const getManyAndCountMock = jest.fn() .mockReturnValueOnce([myDataAllMock, 2]) .mockReturnValueOnce([myDataAllMock[1], 1]); const createQueryBuilderMock = jest.fn(() => ({ leftJoin: jest.fn().mockReturnThis(), take: jest.fn().mockReturnThis(), skip: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), addSelect: jest.fn().mockReturnThis(), andWhere: jest.fn().mockReturnThis(), getManyAndCount: getManyAndCountMock // 所有实例复用同一个mock })); let service: MyService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ MyService, { provide: MY_REPOSITORY, useValue: { createQueryBuilder: createQueryBuilderMock, } } ], }).compile(); service = module.get<MyService>(MyService); }); // 测试用例保持不变 describe('getAll', () => { // ...你的测试代码 }); // 可选:在所有测试完成后重置mock,避免影响其他测试文件 afterAll(() => { getManyAndCountMock.mockReset(); });
注意:这种方式依赖mock返回值的调用顺序,如果后续新增测试用例,需要调整mockReturnValueOnce的队列,灵活性不如第一种方案。
内容的提问来源于stack exchange,提问作者Jun
相关产品推荐
相关产品推荐

