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

使用QueryRunner事务的TypeORM Jest测试并行执行失败

问题描述

尝试用TypeORM(搭配PostgreSQL)编写Jest测试,期望测试能并行执行,且每个测试启动事务、执行后回滚事务。当前测试配置及遇到的问题如下:

当前测试代码

let queryRunner: QueryRunner
let em: EntityManager;

beforeAll(async () => {
  const testAppDataSource = await getTestAppDataSource()
  queryRunner = testAppDataSource.createQueryRunner()
  queryRunner.connect()
})

beforeEach(async () => {
  await queryRunner.startTransaction()
  em = queryRunner.manager
})

afterEach(async () => {
  await queryRunner.rollbackTransaction()
})

afterAll(async () => {
  await queryRunner.release()
})

test("testname", async() => {
  // perform operations with em
})

getTestAppDataSource 实现

const testAppDataSource = new DataSource(dataSourceOptions)

export const getTestAppDataSource = async () => {
  if (!testAppDataSource.isInitialized) {
    await testAppDataSource.initialize()
  }
  return testAppDataSource
}

报错信息

使用--runInBand串行执行时一切正常,但并行执行时出现以下错误:

QueryFailedError: duplicate key value violates unique constraint "pg_type_typname_nsp_index"

TypeError: Cannot read properties of undefined (reading 'rollbackTransaction')

通常仅有1个测试能成功,怀疑测试间存在资源干扰,猜测dataSource.createQueryRunner()给所有测试返回了同一个QueryRunner实例。

补充说明:曾尝试将const testAppDataSource = await getTestAppDataSource()替换为以下代码,但问题依然存在:

const testAppDataSource = new DataSource(testDataSourceOptions)
await testAppDataSource.initialize()

解决方案

问题核心在于多个并行测试共享了同一个QueryRunner和数据源实例,导致事务操作冲突、资源竞争。Jest并行执行时,每个测试文件会在独立进程中运行,但模块级单例变量会被缓存,同时全局的queryRunner变量会被多个测试用例同时修改,引发未定义错误。

1. 为每个测试文件创建独立的数据源和QueryRunner

不要使用单例数据源,而是在每个测试文件中初始化独立的数据源实例,并且为每个测试用例创建专属的QueryRunner:

let queryRunner: QueryRunner;
let em: EntityManager;
let dataSource: DataSource;

beforeAll(async () => {
  // 每个测试文件初始化独立的数据源
  dataSource = new DataSource({
    ...dataSourceOptions,
    // 可选:调整连接池大小,避免并行测试耗尽连接
    poolSize: 10,
  });
  await dataSource.initialize();
});

beforeEach(async () => {
  // 每个测试用例创建新的QueryRunner
  queryRunner = dataSource.createQueryRunner();
  await queryRunner.connect();
  await queryRunner.startTransaction();
  em = queryRunner.manager;
});

afterEach(async () => {
  // 每个测试用例回滚事务并释放QueryRunner
  if (queryRunner) {
    await queryRunner.rollbackTransaction();
    await queryRunner.release();
  }
});

afterAll(async () => {
  // 测试文件结束后销毁数据源
  await dataSource.destroy();
});

test("testname", async () => {
  // 使用当前测试用例的em执行操作
});

2. 移除模块级单例数据源

删除原有的getTestAppDataSource单例实现,确保每个测试文件都能创建独立的数据源实例,避免Jest模块缓存导致的共享问题。

3. 禁用Jest模块缓存(可选)

如果依然存在模块缓存问题,可以在测试文件顶部添加:

jest.resetModules();

或者在Jest配置文件中设置resetModules: true,确保每个测试文件加载时模块都被重新初始化。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:04:53