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

Jest中createQueryBuilder链式mockReturnValueOnce失效问题排查

问题原因分析

你遇到的问题核心在于:每次调用createQueryBuilderMock时,都会生成一个全新的getManyAndCount mock函数实例。

原来的代码中,createQueryBuilderMock是一个工厂函数,每次被调用(也就是你的service里调用repository.createQueryBuilder()时),都会返回一个包含新getManyAndCount mock的对象。第一个测试用例消耗了该mock的第一个mockReturnValueOnce,但第二个测试用例调用createQueryBuilder()时,得到的是另一个全新的getManyAndCount mock,它的返回值队列是从头开始的,所以依然会返回[myDataAllMock, 2]。

解决方案

推荐采用每个测试用例独立控制mock返回值的方式,既避免跨测试污染,也更灵活。具体步骤如下:

  1. 在beforeEach中动态创建mock,将getManyAndCount的mock变量暴露出来,方便测试用例修改
  2. 每个测试用例单独设置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:14:59