非NestJS项目中Jest Mock TypeORM DataSource单元测试问题
解决TypeORM非NestJS项目中Mock DataSource事务单元测试问题
你遇到的核心问题是jest.mock的使用时机和目标错误,导致实际的DataSource实例被导入并尝试连接数据库,从而抛出Driver not Connected错误。下面是具体的修复方案:
问题根源
jest.mock会被提升到模块代码的最顶部执行,你在beforeEach里调用的mock不会覆盖之前已经导入的datasource实例。- 业务代码导入的是
datasource.ts中已经实例化好的datasource对象,而非TypeORM的DataSource类,所以mocktypeorm模块的DataSource构造函数完全无效。
修复步骤
1. 调整Mock的位置与目标
直接在测试文件顶部mock你的自定义datasource模块,确保业务代码导入的是mock后的实例,而非真实数据库连接。
2. 确保Mock的异步方法返回Promise
事务相关的方法(startTransaction、rollbackTransaction等)都是异步操作,需要让它们返回resolved的Promise,避免测试中出现未处理的rejection。
3. 获取Mock实例用于验证
在测试中获取mock后的datasource实例,方便后续验证方法调用情况。
修改后的完整代码
mock.datasource.ts(优化异步方法)
import { DataSource } from 'typeorm'; export const dataSourceMockFactory: () => MockType<DataSource> = jest.fn(() => ({ createQueryRunner: jest.fn().mockReturnValue({ startTransaction: jest.fn().mockResolvedValue(undefined), // 明确返回resolved Promise rollbackTransaction: jest.fn().mockResolvedValue(undefined), release: jest.fn().mockResolvedValue(undefined), manager: { getRepository: jest.fn().mockReturnValue({ create: jest.fn(), findOne: jest.fn().mockResolvedValue(getMyDummyObject()), // 异步方法返回Promise }), save: jest.fn().mockResolvedValue(undefined), }, }), })); export type MockType<T> = { [P in keyof T]?: jest.Mock; };
dummy.unit.test.ts(核心修改)
import { dummyService } from './dummy.service'; import { datasource } from './../../db/datasource'; import { dataSourceMockFactory } from './mock.datasource'; // 关键:把jest.mock放到模块顶部,mock自定义的datasource模块 jest.mock('./../../db/datasource', () => ({ datasource: dataSourceMockFactory(), })); describe('dummy service test', () => { // 类型断言为Mocked,方便调用mock方法 const mockDatasource = datasource as jest.Mocked<typeof datasource>; afterEach(() => { jest.clearAllMocks(); }); it('should execute transaction without error', async () => { await expect(dummyService()).resolves.not.toThrow(); // 验证queryRunner相关方法的调用情况 expect(mockDatasource.createQueryRunner).toHaveBeenCalled(); const queryRunner = mockDatasource.createQueryRunner.mock.results[0].value; expect(queryRunner.startTransaction).toHaveBeenCalled(); expect(queryRunner.release).toHaveBeenCalled(); }); it('should rollback transaction on error', async () => { // 模拟findOne抛出错误 const queryRunner = mockDatasource.createQueryRunner.mock.results[0].value; queryRunner.manager.getRepository().findOne.mockRejectedValue(new Error('DB Error')); await expect(dummyService()).resolves.not.toThrow(); // 验证回滚方法被调用 expect(queryRunner.rollbackTransaction).toHaveBeenCalled(); expect(queryRunner.release).toHaveBeenCalled(); }); });
额外注意事项
- 业务代码中的
dummyService如果硬编码了someId,建议改成参数传入,方便测试时控制输入。 - 确保
getMyDummyObject()返回的数据结构与MyObject实体一致,避免类型或结构不匹配的问题。 - 若需验证数据库操作的参数(比如
findOne的查询条件),可以通过mock.calls获取调用参数进行断言。
内容的提问来源于stack exchange,提问作者Kit A.
相关产品推荐
相关产品推荐

