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

如何在NestJS中用Jest测试TypeScript函数并Mock createQueryBuilder?

用Jest测试NestJS中的日期转换与批量更新函数

核心测试要点

你的代码核心逻辑分为两部分:

  • 将指定字段(beginDate/endDate/planningDate/executionDate/releaseDate)的字符串值转换为Date对象
  • 遍历传入对象的每个键,调用Repository的链式更新语句执行批量更新
    测试时需要分别验证这两个逻辑,重点解决createQueryBuilder链式调用的Mock问题。

如何Mock createQueryBuilder链式调用

NestJS Repository的createQueryBuilder是链式调用模式,需要Mock每个后续方法(update/set/where/execute),让它们返回自身以维持调用链,最后让execute返回预期的Promise结果。

基础Mock模板

在测试文件中先Mock你的Repository:

// 假设你的业务服务是ItemService
const mockItemRepository = {
  createQueryBuilder: jest.fn(() => ({
    update: jest.fn().mockReturnThis(),
    set: jest.fn().mockReturnThis(),
    where: jest.fn().mockReturnThis(),
    execute: jest.fn().mockResolvedValue({ affected: 1 }), // 默认返回成功更新结果
  })),
};

// 在测试模块中注入Mock实例
beforeEach(async () => {
  const module: TestingModule = await Test.createTestingModule({
    providers: [
      ItemService,
      { provide: getRepositoryToken(Item), useValue: mockItemRepository },
    ],
  }).compile();

  service = module.get<ItemService>(ItemService);
});

完整测试用例示例

测试1:日期字段正确转换且更新语句正常执行

it('should convert date fields to Date objects and execute updates for each key', async () => {
  // 构造测试数据:包含日期字符串和普通字段
  const testItem = {
    beginDate: '2024-01-01',
    name: 'Test Item',
    executionDate: '2024-02-01',
  };
  const testId = 1;

  // 调用目标函数(假设它是ItemService的updateItemFields方法)
  await service.updateItemFields(testId, testItem);

  // 验证日期字段是否被正确转换
  expect(testItem.beginDate instanceof Date).toBe(true);
  expect(testItem.executionDate instanceof Date).toBe(true);
  expect(testItem.name).toBe('Test Item'); // 非日期字段保持原值

  // 验证createQueryBuilder被调用的次数等于item的键数量
  expect(mockItemRepository.createQueryBuilder).toHaveBeenCalledTimes(Object.keys(testItem).length);

  // 验证更新语句的参数正确性
  expect(mockItemRepository.createQueryBuilder().set).toHaveBeenCalledWith(expect.objectContaining({
    beginDate: expect.any(Date),
  }));
  expect(mockItemRepository.createQueryBuilder().where).toHaveBeenCalledWith('id = :id', { id: testId });
});

测试2:更新失败时抛出HttpException

it('should throw HttpException when update operation fails', async () => {
  const testItem = { name: 'Test Item' };
  const testId = 1;
  const mockDbError = new Error('Connection timeout');

  // 修改Mock,让execute方法抛出错误
  mockItemRepository.createQueryBuilder().execute.mockRejectedValueOnce(mockDbError);

  // 验证是否抛出预期的异常
  await expect(service.updateItemFields(testId, testItem)).rejects.toThrow(HttpException);
  await expect(service.updateItemFields(testId, testItem)).rejects.toMatchObject({
    response: { message: 'Error when trying to update Item' },
  });
});

关键注意事项

  • 每次测试后重置Mock:在afterEach中调用jest.clearAllMocks(),避免不同测试用例的Mock状态互相干扰
  • 链式调用的Mock一致性:确保createQueryBuilder返回的对象中,每个链式方法都通过mockReturnThis()维持调用链
  • 日期类型验证:使用expect.any(Date)验证转换后的类型即可,无需精确匹配具体日期值

内容的提问来源于stack exchange,提问作者Victor Guidi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 08:40:35