Jest 导入函数文件触发「Cannot log after tests are done」报错
问题排查与解决方案
核心原因
该报错本质是你导入的activityItemFind模块在顶层代码执行阶段(也就是require加载的瞬间)就触发了Sequelize的异步数据库连接操作,没有等到你调用导出函数的时候才初始化连接。报错里的SELECT 1+1 AS result是Sequelize调用authenticate方法做连接验证时默认执行的语句,刚好对应你触发的日志内容。
此前导入不会报错,大概率是该模块之前的顶层没有执行数据库连接相关逻辑,是后续改动新增的。
排查步骤
- 第一步先打开
../../game/activities/activityItemFind文件,检查所有不在函数内部的顶层代码:- 查找有没有直接调用
sequelize.authenticate()、sequelize.sync()的代码 - 查找有没有直接执行模型查询、或者初始化Sequelize实例时配置了自动触发连接测试的逻辑
- 查找有没有直接调用
- 按照Jest提示执行命令,加上
--detectOpenHandles参数运行测试,确认持有的未释放句柄确实是PostgreSQL数据库连接:jest your-test-file.spec.js --detectOpenHandles
解决方法
方案1:修改业务模块的初始化逻辑(最推荐)
把模块顶层的Sequelize初始化/连接逻辑挪到函数内部,或者导出单独的初始化方法,需要的时候主动调用,不要在模块加载时自动执行:
// 错误写法(顶层直接执行连接) const sequelize = new Sequelize(/* 数据库配置 */) sequelize.authenticate() // 修正后写法 const sequelize = new Sequelize(/* 数据库配置 */) // 导出初始化方法,按需调用 async function initDB() { await sequelize.authenticate() } module.exports = { initDB, _callXTimes, _itemFind, // 其余原有导出项 }
方案2:测试用例中增加生命周期钩子处理数据库连接
如果不方便修改业务代码,可以在测试文件中增加afterAll钩子,测试跑完后主动关闭Sequelize连接,释放句柄:
// 导入模块中暴露的sequelize实例 const { sequelize, _callXTimes, _itemFind, _chancePTFind, activityItemFind } = require('../../game/activities/activityItemFind'); // 所有测试执行完成后关闭数据库连接 afterAll(async () => { await sequelize.close(); }) // 你的原有测试用例
方案3:Mock掉Sequelize依赖
单元测试不需要连接真实数据库的场景下,可以直接Mock整个Sequelize模块,避免触发真实连接:
// 测试文件最顶部加入mock逻辑,需要在导入业务模块之前执行 jest.mock('sequelize', () => { const mockSequelize = { authenticate: jest.fn(), close: jest.fn(), define: jest.fn(), query: jest.fn(), }; return jest.fn(() => mockSequelize); }); // 再导入你需要的业务模块 const { _callXTimes, _itemFind, _chancePTFind, activityItemFind } = require('../../game/activities/activityItemFind'); // 你的原有测试用例
内容的提问来源于stack exchange,提问作者Bryson Kruk
相关产品推荐
相关产品推荐

