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
相关产品推荐
相关产品推荐

