如何在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
相关产品推荐
相关产品推荐

