如何用Jest模拟TypeORM的getManager与事务操作
如何用Jest模拟TypeORM的getManager和事务方法做单元测试
刚好之前做过类似的单元测试场景——要完全隔离TypeORM的底层数据库连接,只验证业务逻辑里的事务流程和模型保存调用,下面是我实践下来可行的方案,分步骤拆解:
1. 先Mock整个TypeORM模块的getManager方法
我们需要避免实际调用TypeORM的ConnectionManager,所以用Jest的jest.mock来接管TypeORM模块,只替换我们需要控制的getManager方法,保留其他原生功能(如果需要的话):
import { getManager } from "typeorm"; // Mock TypeORM模块,保留其他未指定的原生方法 jest.mock("typeorm", () => ({ ...jest.requireActual("typeorm"), getManager: jest.fn(), }));
2. 模拟EntityManager和事务逻辑
TypeORM的transaction方法会接收一个回调函数,这个回调会拿到事务内的EntityManager,所以我们需要模拟两层EntityManager:外层的getManager()返回的实例,以及事务回调里的实例。
// 模拟事务内的EntityManager,重点mock save方法 const mockTransactionalEntityManager = { save: jest.fn().mockResolvedValue({}), // 可以根据测试需求返回模拟的模型实例 }; // 模拟外层的EntityManager,mock transaction方法 const mockEntityManager = { transaction: jest.fn().mockImplementation(async (callback) => { // 调用传入的事务回调,把模拟的事务EntityManager传进去 return callback(mockTransactionalEntityManager); }), }; // 让getManager返回我们模拟的EntityManager (getManager as jest.Mock).mockReturnValue(mockEntityManager);
3. 编写测试用例并验证调用
现在可以编写测试用例,调用目标方法后,验证所有mock方法的调用是否符合预期:
describe("你的目标业务方法", () => { // 每个测试用例前重置所有mock,避免用例间污染 beforeEach(() => { jest.clearAllMocks(); }); it("应该在单个事务中依次保存两个模型", async () => { // 准备测试用的模型数据 const testFirstModel = { id: 1, name: "测试模型1" }; const testSecondModel = { id: 2, name: "测试模型2" }; // 调用目标业务方法 await yourTargetBusinessMethod(testFirstModel, testSecondModel); // 验证getManager被调用了一次 expect(getManager).toHaveBeenCalledTimes(1); // 验证transaction方法被触发 expect(mockEntityManager.transaction).toHaveBeenCalledTimes(1); // 验证两次save都被调用,且传入了正确的模型参数 expect(mockTransactionalEntityManager.save).toHaveBeenCalledTimes(2); expect(mockTransactionalEntityManager.save).toHaveBeenNthCalledWith(1, testFirstModel); expect(mockTransactionalEntityManager.save).toHaveBeenNthCalledWith(2, testSecondModel); }); // 可选:测试事务失败的场景 it("当第一个模型保存失败时,应该终止事务并抛出错误", async () => { const testFirstModel = { id: 1, name: "测试模型1" }; const testSecondModel = { id: 2, name: "测试模型2" }; // 让第一次save调用抛出错误 mockTransactionalEntityManager.save.mockRejectedValueOnce(new Error("保存失败")); // 验证方法会抛出预期错误 await expect(yourTargetBusinessMethod(testFirstModel, testSecondModel)).rejects.toThrow("保存失败"); // 验证第二次save没有被调用(事务终止) expect(mockTransactionalEntityManager.save).toHaveBeenCalledTimes(1); }); });
关键注意点
- 一定要用
jest.clearAllMocks()在每个测试用例前重置mock状态,防止不同用例的调用记录互相干扰。 - 如果你的业务逻辑里依赖TypeORM的其他方法,可以在
jest.mock里继续添加对应的mock实现,或者保留原生实现。 - 模拟
transaction时,必须正确调用传入的回调函数,否则业务逻辑里的事务代码不会执行。
内容的提问来源于stack exchange,提问作者Hatchmaster J
相关产品推荐
相关产品推荐

