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

集成测试因fixture与TypeORM问题本地通过但GitHub Actions报错

问题现象

本地运行所有测试套件全部正常通过,但GitHub Actions CI校验始终失败,问题仅出现在CI环境,本地无法复现。
失败用例报错为TypeError: Cannot read properties of undefined (reading 'id'),触发位置是读取查询返回结果的id属性时,说明按programId查询workout的方法返回了空数组。

相关代码
  • 问题测试用例:路径src/workout/repositories/workout.typeorm.repository.inte.ts
it('should find workout by program id', async () => {
  const workoutData = workoutDataBuilder({
    id: HardCodedValuesEnum.workoutId,
    title: 'Leg Workout',
  })
  const workout = new Workout(workoutData)
  const program = new Program(programFixture)
  program.workouts = [workout]
  await programRepository.save(program)
  const expectedWorkout = await workoutRepository.save(workout)

  const [receivedWorkout] = await workoutRepository.findByProgramId(
    HardCodedValuesEnum.programId,
  )

  expect(receivedWorkout.id).toStrictEqual(expectedWorkout.id)
})
  • 仓储层实现:路径src/workout/repositories/workout.typeorm.repository.ts
@EntityRepository(Workout)
export class TypeOrmWorkoutRepository
  extends Repository<Workout>
  implements WorkoutRepository
{
  findByProgramId(programId: string): Promise<Workout[]> {
    const workout = this.find({
      relations: ['program'],
      where: {
        program: {
          id: programId,
        },
      },
    })

    return Promise.resolve(workout)
  }
}
  • GitHub Actions工作流配置:
name: Tests

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:

  backend-tests:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: back
    steps:
    - uses: actions/checkout@v2
    - name: Use Node.js ${{ matrix.node-version }}
      uses: actions/setup-node@v2
      with:
        node-version: ${{ matrix.node-version }}
        cache: 'yarn'
        cache-dependency-path: '**/back/yarn.lock'
    - run: sudo /etc/init.d/mysql start
    - run: mysql -e 'CREATE DATABASE corposano' -uroot -proot
    - run: mysql -e 'ALTER USER 'root'@'localhost' IDENTIFIED BY ""' -uroot -proot
    - run: yarn
    - run: yarn build
    - run: yarn jest
    - run: yarn test:inte
    - run: yarn test:e2e
CI报错日志
Run yarn test:inte
yarn run v1.22.19
$ jest --testTimeout=10000 -c jest-inte.json --runInBand --forceExit
FAIL src/workout/repositories/workout.typeorm.repository.inte.ts (10.821 s)
  ● TypeOrm Workout Repository › should find workout by program id
    TypeError: Cannot read properties of undefined (reading 'id')
      158 |     )
      159 |
    > 160 |     expect(receivedWorkout.id).toStrictEqual(expectedWorkout.id)
          |                            ^
      161 |   })
      162 |
      163 |   it.each([
      at Object.<anonymous> (src/workout/repositories/workout.typeorm.repository.inte.ts:160:28)
PASS src/athlete/repositories/typeorm-athlete.repository.inte.ts
PASS src/exercise/repositories/type-orm-exercise.repository.inte.ts
PASS src/program/repositories/type-orm-program.repository.inte.ts
PASS src/daily-task/repositories/typeorm-workout.typeorm.repository.inte.ts
PASS src/exercise/repositories/type-orm-exercise-template.repository.inte.ts
Test Suites: 1 failed, 5 passed, 6 total
Tests:       1 failed, 22 passed, 23 total
Snapshots:   0 total
Time:        20.43 s
Ran all test suites.
根因定位与CI调试方案

优先排查高概率问题

  • 关联保存逻辑缺陷:当前代码先给program实例挂载workouts数组再save program,之后又单独save workout。TypeORM在保存关联关系时,不同版本、不同配置下的级联保存行为不一致:如果Program实体的workouts字段没有配置cascade: true,单独save program不会自动把workout和program的关联外键写入workout表。本地可能因为数据库有历史残留数据、TypeORM版本和CI有小差异,导致关联能查到;CI环境每次都是全新数据库,关联没存上自然查不到结果。
    修复方式:要么给Program的workouts关联加上级联配置,去掉后面单独save workout的逻辑;要么保存完两个实体后,手动更新workout的programId字段再保存,不要依赖隐式的关联保存。
  • 运行环境版本不一致:当前workflow配置里引用了${{ matrix.node-version }},但没有在job下定义matrix配置,CI环境会安装Node.js的默认版本,和本地开发用的Node版本可能存在差异,部分依赖包在不同Node版本下的行为会有区别。同时要对比本地和CI安装的TypeORM版本,yarn缓存问题可能导致CI装的小版本和本地不一致,关联查询逻辑存在变动。
  • 数据库初始化时序问题:CI里的MySQL启动后直接执行改密码、建库命令,没有等MySQL完全就绪,CI环境MySQL启动速度比本地慢,可能存在建库命令执行时服务还没完全启动,库表初始化不完全,后续数据写入失败。
  • 默认配置差异:CI用的是Ubuntu源安装的MySQL,默认排序规则、大小写敏感策略和本地Windows/Mac环境的MySQL可能不一致,会导致关联查询匹配失败。另外当前查询没有加排序规则,不要依赖数据库默认返回顺序。

CI环境实操调试方法

  • 在测试用例里加临时日志,把programRepository.save、workoutRepository.save的返回结果,以及findByProgramId的查询结果全量打印,看CI环境保存数据时关联外键到底有没有写入、查询返回的数组长度是多少。
  • 在执行test:inte步骤前加一步,执行yarn list --pattern typeorm打印TypeORM版本,和本地版本做比对,确认依赖版本一致。
  • 替换原来的MySQL启动命令,加循环等待逻辑,确认MySQL端口就绪后再执行建库、改密码操作,避免数据库初始化不完全。
  • 测试用例里加前置断言,在读取id之前先断言查询结果长度为1,比如expect(await workoutRepository.findByProgramId(HardCodedValuesEnum.programId)).toHaveLength(1),可以更明确地定位失败原因。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:36:22