使用Jest测试代码库函数时数据库连接异常解决方法
解决方案
首先明确两个核心问题的根因:
- 报
role 'dan' does not exist是因为测试运行时db.js里的knex配置没有指定明确的数据库用户名,Postgres驱动默认取当前系统登录用户名dan作为连接角色,而你的本地PG实例中没有创建这个同名角色,直接触发连接错误。 - 跨测试文件无法复用全局db对象本质是Jest的模块沙箱+环境配置缺失问题,完全不需要每个测试文件重建连接,也不需要给所有业务函数新增db入参,下面两个方案都是生产环境验证过的成熟实践,改造成本极低:
方案1:环境隔离+全局连接管理(最推荐,零业务侵入)
这个方案不用改任何业务代码逻辑,只需要调整配置和测试初始化逻辑:
- 第一步:改造
db.js的配置逻辑,根据NODE_ENV环境变量动态加载对应环境的连接参数
不要把连接配置硬编码,优先从环境变量读取,测试环境启动时自动注入测试库的连接信息(提前在本地PG建好测试库、对应账号并授权,从根源上避免默认取系统用户名当角色的问题),示例:// db.js const knex = require('knex'); const config = { development: { /* 开发库配置 */ }, test: { client: 'pg', connection: { host: process.env.DB_HOST || '127.0.0.1', // 明确指定测试库账号,不要留空让驱动取系统用户名 user: process.env.DB_TEST_USER || 'test_user', password: process.env.DB_TEST_PWD || 'test123', database: process.env.DB_TEST_NAME || 'service_test' } }, production: { /* 生产库配置 */ } }; const db = knex(config[process.env.NODE_ENV || 'development']); module.exports = { db }; - 第二步:在Jest配置中增加全局初始化/销毁逻辑,全测试周期复用同一个连接
在jest.config.js中配置全局钩子:
三个钩子的职责非常清晰:module.exports = { // 其他原有配置 globalSetup: '<rootDir>/test/global-setup.js', globalTeardown: '<rootDir>/test/global-teardown.js', setupFilesAfterEnv: ['<rootDir>/test/setup-after-env.js'] }global-setup.js:跑所有测试前执行一次,设置NODE_ENV=test,执行数据库迁移、灌入基础测试种子数据setup-after-env.js:每个测试文件加载前执行,注册全局afterEach钩子,每个用例跑完后清理对应业务表的测试数据,避免用例间数据污染global-teardown.js:所有测试跑完后执行,关闭knex连接,销毁连接池,避免进程挂住
这套配置跑完,所有业务代码里直接导入的db对象就是测试环境的合法连接,完全不会出现连接错误,也不需要每个测试文件重复创建连接实例。
方案2:Jest模块Mock(适合不想调整现有db配置的场景)
如果你暂时不想改db.js的原有逻辑,用Jest自带的模块Mock能力也能解决问题,同样不需要改业务函数的入参:
- 在项目根目录的
__mocks__文件夹下新建db.js文件,导出连接到测试库的knex实例:// __mocks__/db.js const knex = require('knex'); const testDb = knex({ client: 'pg', connection: { host: '127.0.0.1', user: 'test_user', // 提前在PG中创建该角色并授权测试库权限 password: 'test123', database: 'service_test' } }); module.exports = { db: testDb }; - 在Jest配置中开启自动Mock,或者在需要测试的文件顶部手动加
jest.mock('../path/to/db.js'),Jest会自动把所有业务代码中导入的db对象替换成你在mock里初始化的测试库连接,业务代码完全无感知。
常见误区避坑
- 不要每个测试文件重复创建knex连接:单测跑的时候文件是隔离加载的,重复建连接会快速占满PG的连接数,还会拖慢测试速度,全局复用连接是标准做法。
- 不要为了测试给所有业务函数加db入参:这种硬改函数签名的方式侵入性太强,后续维护成本极高,用环境隔离或者模块Mock的方式完全可以达到依赖注入的效果,不需要改业务代码。
- 测试库一定要和开发库、生产库物理隔离,不要拿开发库跑测试,避免测试清理数据的时候误删正常业务数据。
内容的提问来源于stack exchange,提问作者Dan
相关产品推荐
相关产品推荐

