如何在Jest中为跨文件的测试组统一初始化数据库?
问题原因
Jest运行测试分为两个固定阶段:
- 测试收集阶段:同步执行所有顶层/嵌套的
describe回调、测试分组注册逻辑,提前把所有it/test用例收集到调度队列中。这个阶段beforeAll钩子还未触发,你此时传入分组函数的sequelize还是初始的null值。 - 测试执行阶段:所有用例收集完成后,才会按顺序执行
beforeAll、测试用例、afterAll钩子,此时sequelize才会被正确赋值。
解决方案
方案1:测试执行阶段再读取实例
不要在测试注册阶段就把sequelize传入分组函数,让分组内的测试用例在执行时再读取外层作用域的sequelize变量即可:
// 改造前的分组函数(注册阶段就依赖sequelize,不可行) // function testGroupOne(sequelize) { // return () => { // it('测试用户模型存在', () => { // expect(sequelize.models.User).toBeDefined() // }) // } // } // 改造后的分组函数,执行阶段再读变量 function testGroupOne() { return () => { it('测试用户模型存在', () => { // 测试执行时beforeAll已经跑完,sequelize已经完成赋值 expect(sequelize.models.User).toBeDefined() }) } } // 入口测试文件调整后代码 let sequelize = null beforeAll(async () => { sequelize = await initDataBase(config) }) afterAll(async () => { await sequelize.drop() }) describe('DB testing', () => { it('Test #0', () => expect(true).toEqual(true)) // 不需要提前传sequelize参数 testGroupZero()() describe('Test Group #1', testGroupOne()) describe('Test Group #2', testGroupTwo()) })
方案2:跨文件共享实例用global挂载
如果你的测试分组分散在不同文件,无法共享外层作用域变量,可以把初始化后的sequelize挂载到Jest的全局对象上:
// 入口测试/公共setup文件 beforeAll(async () => { global.sequelize = await initDataBase(config) }) afterAll(async () => { await global.sequelize.drop() })
各个测试分组的文件内直接读取global.sequelize即可,执行时就能拿到初始化完成的实例。
方案3:全局统一初始化用setupFilesAfterEnv配置
如果要给所有测试文件统一加初始化逻辑,不需要每个文件重复写beforeAll/afterAll,可以修改Jest配置:
- 在
jest.config.js中添加配置项:
module.exports = { // 其余配置保持不变 setupFilesAfterEnv: ['<rootDir>/test/global-setup.js'] }
- 新建
test/global-setup.js编写全局初始化逻辑:
const { initDataBase } = require('../src/db') const config = require('../src/config/db') beforeAll(async () => { global.sequelize = await initDataBase(config) }) afterAll(async () => { await global.sequelize.drop() await global.sequelize.close() })
配置后所有测试文件都会自动执行这组全局钩子,不用每个文件单独引入。
注意事项
Jest默认开启多进程并行执行测试,每个测试worker进程都会独立执行一遍setup逻辑。如果你需要全局只初始化一次数据库,可以在执行测试时添加--runInBand参数强制单进程执行,或者把数据库初始化逻辑放到Jest的globalSetup配置中,仅在所有测试启动前执行一次。
内容的提问来源于stack exchange,提问作者Artyom Ionash
相关产品推荐
相关产品推荐

