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

Jest测试环境下无法实例化Sequelize模型的问题求助

问题根因

问题核心来自Jest的进程隔离机制:

  • globalSetup 是在Jest的主进程中独立执行的,执行完成后才会启动多个测试worker进程,两个上下文完全隔离。你在globalSetup中初始化的Sequelize连接、挂载的模型实例,仅存在于主进程内存中,测试worker进程加载的Sequelize、repository模块是全新的实例,从未完成初始化,所以调用模型方法时会报undefined错误。
  • 另外你启动PostgreSQL容器后拿到的随机映射端口,也没有同步给测试进程,测试进程连数据库的端口还是默认配置,根本连不上你启动的测试容器,这也是模型无法初始化的隐形原因。
修复步骤

1. 调整globalSetup逻辑,仅保留容器启动操作

修改你的jest.setup.js,仅做容器启动,同时把容器的连接参数同步给测试进程:

import { GenericContainer, Wait } from 'testcontainers';
import fs from 'fs';
import path from 'path';

module.exports = async () => {
  process.env.NODE_ENV = 'test';
  // 启动PostgreSQL容器
  const container = await new GenericContainer('postgres')
    .withEnv('POSTGRES_USER', 'test')
    .withEnv('POSTGRES_PASSWORD', 'test')
    .withEnv('POSTGRES_DB', 'my-database')
    .withExposedPorts(5432)
    .withWaitStrategy(Wait.forLogMessage('database system is ready to accept connections'))
    .start();
  
  // 把容器连接参数写入临时文件,供测试进程读取
  const dbConfig = {
    host: container.getHost(),
    port: container.getMappedPort(5432),
    username: 'test',
    password: 'test',
    database: 'my-database'
  };
  fs.writeFileSync(path.resolve(__dirname, 'test-db-config.json'), JSON.stringify(dbConfig));

  // 全局挂载容器实例,测试完成后可在globalTeardown中关闭
  global.testDbContainer = container;
};

新增jest.teardown.js配置,测试完成后关闭容器,同时删除临时配置文件:

import fs from 'fs';
import path from 'path';

module.exports = async () => {
  if (global.testDbContainer) {
    await global.testDbContainer.stop();
  }
  fs.unlinkSync(path.resolve(__dirname, 'test-db-config.json'));
};

在jest.config.js中新增teardown配置:

globalTeardown: '<rootDir>/tests/jest.teardown.js',

2. 新增测试进程初始化逻辑,完成数据库连接与模型挂载

新增jest.init-db.js文件,放在tests目录下:

import fs from 'fs';
import path from 'path';
import {
  initializeConectionDatabaseOnly, runPendingMigration, runPendingSeed,
} from '../src/db/database';
// 导入你的Sequelize实例
import sequelize from '../src/db/models';

// 读取容器配置,覆盖环境变量中的数据库连接参数
const dbConfig = JSON.parse(fs.readFileSync(path.resolve(__dirname, 'test-db-config.json')));
process.env.DB_HOST = dbConfig.host;
process.env.DB_PORT = dbConfig.port;
process.env.DB_USER = dbConfig.username;
process.env.DB_PASS = dbConfig.password;
process.env.DB_NAME = dbConfig.database;

// 所有测试执行前,初始化Sequelize连接、挂载模型、执行迁移和种子
beforeAll(async () => {
  await initializeConectionDatabaseOnly();
  await runPendingMigration();
  await runPendingSeed();
});

// 所有测试执行后,关闭数据库连接
afterAll(async () => {
  await sequelize.close();
});

在jest.config.js中新增配置,让这个初始化文件在每个测试worker进程中执行:

setupFilesAfterEnv: ['<rootDir>/tests/jest.init-db.js'],

3. 关闭Jest并行执行,避免多进程冲突

因为你只有一个测试数据库容器,多个测试worker同时操作会出现数据冲突,启动测试时加上--maxWorkers=1参数,修改package.json的test命令:

"test": "jest --maxWorkers=1"

4. 可选:增加测试用例的数据隔离

如果需要每个测试用例的数据互不影响,可以在每个测试执行前后开启/回滚事务,加在jest.init-db.js中即可:

beforeEach(async () => {
  await sequelize.transaction();
});

afterEach(async () => {
  await sequelize.rollback();
});

内容的提问来源于stack exchange,提问作者Arthur Henrique Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 11:57:02