knex-mock-client单元测试报TypeError: insertLog is not a function
knex-mock-client + Jest TypeScript单测报
default is not a function解决 问题场景
相关依赖版本
{ "@faker-js/faker": "^7.2.0", "@types/jest": "^28.1.1", "concurrently": "^7.2.1", "jest": "^28.1.1", "knex-mock-client": "^1.8.4", "nodemon": "^2.0.16", "ts-jest": "^28.0.4" }
业务代码(insertRecord.ts)
//insertRecord.ts import {knexInstance} from './logDatabaseConfig'; const insertLog = async(quan: Number) => { try { await knexInstance('LOG') .insert({sync_quantity: quan, sync_at: new Date()}); } catch (error) { throw new Error('Error insert' + error); } } export default insertLog;
原测试代码
import { expect } from '@jest/globals'; import knex, { Knex } from 'knex'; import { Tracker, getTracker, MockClient } from 'knex-mock-client'; import { faker } from '@faker-js/faker'; import insertLog from './insertLog'; // 错误的mock写法 jest.mock('./insertLog', () => { return { db: knex({ client: MockClient }) }; }); describe('log insert tests', () => { let tracker: Tracker; beforeAll(() => { tracker = getTracker(); }); afterEach(() => { tracker.reset(); }); it('should add new log record', async () => { tracker.on.insert('LOG'); const syncQuantity = 1; await insertLog(1); const insertHistory = tracker.history.insert; expect(insertHistory).toHaveLength(1); expect(insertHistory[0].method).toEqual('insert'); }); });
运行后抛出错误:TypeError: (0 , insertLog_1.default) is not a function,错误定位在await insertLog(1);行。
错误根因
- mock目标完全错误:单测不能直接mock被测业务模块本身。当前代码把
./insertLog整个替换成了只带db属性的对象,原模块默认导出的insertLog函数被完全覆盖,导入后拿到的不是函数,调用必然报错。 - mock逻辑不符合knex-mock-client使用要求:knex-mock-client需要mock的是导出knex实例的依赖模块(也就是
./logDatabaseConfig),替换其中的真实knex连接为MockClient实例,而不是替换业务逻辑模块。 - 缺失mock响应配置:原测试中
tracker.on.insert('LOG')没有设置返回值,knex mock调用会返回undefined,容易触发业务代码异常分支。
修复方案
- 修改mock路径,指向knex实例所在的
./logDatabaseConfig模块,不要mock被测的insertLog模块 - 让mock返回的对象结构和原模块导出结构完全匹配,保证业务代码能正常拿到mock的knex实例
- 给tracker监听的insert操作设置mock响应值,避免await拿到undefined触发异常
修复后测试代码
import { expect } from '@jest/globals'; import knex from 'knex'; import { Tracker, getTracker, MockClient } from 'knex-mock-client'; import insertLog from './insertLog'; // 正确mock:替换knex配置模块中的真实实例为mock客户端 jest.mock('./logDatabaseConfig', () => { const mockKnexInstance = knex({ client: MockClient }); return { // 匹配原模块的具名导出knexInstance knexInstance: mockKnexInstance }; }); describe('log insert tests', () => { let tracker: Tracker; beforeAll(() => { tracker = getTracker(); }); afterEach(() => { tracker.reset(); }); it('should add new log record', async () => { // 给insert操作设置mock返回值 tracker.on.insert('LOG').response([]); const syncQuantity = 1; await insertLog(syncQuantity); const insertHistory = tracker.history.insert; expect(insertHistory).toHaveLength(1); expect(insertHistory[0].method).toEqual('insert'); // 可选:校验插入参数是否符合预期 expect(insertHistory[0].bindings[0]).toEqual(syncQuantity); }); });
排查检查点
- 单测中
jest.mock的目标永远是被测模块依赖的外部/内部依赖模块,不能是被测模块本身,否则测试的是mock逻辑,不是真实业务代码 - mock返回的对象结构必须和原模块导出结构完全一致:具名导出对应同名属性,ESM默认导出对应
default属性 - 确认tsconfig.json中开启
"esModuleInterop": true,避免TS默认导入的取值错误 - 所有tracker监听的数据库操作都要设置response返回值,避免mock调用返回undefined导致业务逻辑异常
内容的提问来源于stack exchange,提问作者J L
相关产品推荐
相关产品推荐

