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

NestJS中Jest e2e测试并行执行失败 单独运行正常如何解决

问题根因

你判断的方向完全正确,Jest 默认会根据机器 CPU 核心数并行启动多个 worker 进程执行不同测试文件,你现在的随机报错核心是两个问题叠加:

  • 所有测试套件共用同一个本地 MySQL 的test数据库,并行运行时多个测试进程会同时读写、删除同一份数据
  • 测试数据全部硬编码了固定 ID(比如你代码里反复操作 ID 为 1、2、99 的相机数据),不同套件的操作会互相干扰:比如 A 用例刚插入 ID=1 的相机准备断言删除结果,B 用例的清理逻辑刚好把这条数据删掉,A 的断言就会失败。因为每次测试的进程调度顺序不固定,所以每次失败的用例都不一样。

另外你现有测试代码还有两个隐患:一是afterEach只清理 ID 为 1、2 的数据,其他测试生成的残留数据会慢慢污染测试库,跑的次数越多越容易出奇怪问题;二是每个测试文件独立创建 Nest 应用、新建数据库连接,并行跑的时候很容易打满 MySQL 的最大连接数,触发连接超时类的报错。

修复方案

按优先级从高到低选择:

方案1:彻底做测试环境隔离(长期维护首选)

这是最稳妥的方案,一劳永逸解决所有冲突问题:

  • 不要所有测试套件共用同一个固定的测试库:要么在每个测试套件启动时创建带随机后缀的独立数据库,跑完所有用例后自动删除;要么直接换 SQLite 内存数据库做 e2e 测试,启动速度快,完全不会有跨进程的资源冲突。
  • 测试流程里不要硬编码资源 ID:新建相机的 POST 请求跑完后,从响应体里拿到数据库实际生成的自增 ID,存在局部变量里再拼接后续的删除、查询请求路径,不要直接写死/components/cameras/1这种路径,从根源上避免数据撞车。
  • 每个测试套件启动时先全量清空业务表的残留数据,不要只在afterEach里删固定 ID 的记录。基于 TypeOrm 的清表逻辑可以参考:
beforeAll(async () => {
  // 保留你原有的模块编译、app初始化逻辑
  const moduleFixture: TestingModule = await Test.createTestingModule({
    // 你原来的imports、providers配置不变
  }).compile();

  app = moduleFixture.createNestApplication();
  await app.init();

  // 新增:初始化前清空所有业务表数据
  const dataSource = app.get(DataSource);
  for (const entityMeta of dataSource.entityMetadatas) {
    const repo = dataSource.getRepository(entityMeta.name);
    await repo.clear();
  }
})

方案2:临时关闭Jest并行执行(快速验证问题用)

如果想最快确认是不是并行冲突导致的问题,可以直接修改package.json里的test:e2e脚本,加上--runInBand参数让 Jest 串行执行所有测试文件,改完之后脚本示例:

"test:e2e": "jest --config ./test/jest-e2e.json --runInBand"

这个方案改造成本最低,但是测试执行速度会明显变慢,而且硬编码 ID、数据残留的问题没有彻底解决,后续加新用例还是容易出问题,只适合临时排查用。

方案3:用事务回滚隔离用例数据

如果一定要用同一个 MySQL 库跑所有测试,可以给每个测试用例加事务包裹:用例执行前手动开启数据库事务,用例跑完不管成功失败都直接回滚事务,所有写操作都不会真正持久化到数据库,不同用例、不同套件之间不会互相干扰。要注意如果你的业务代码里本身用了显式事务,需要调整事务传播配置,避免嵌套事务冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 15:27:15