Jest结合NodeJS:未用this时导入模块函数无法Mock的原因
问题解析:为何Mock模块函数后,必须通过this调用才生效
场景还原
你在测试AWS Lambda handler时遇到了这个问题:
- 直接在handler内部调用
myFunc时,Jest的Mock不生效; - 给调用加上
this.前缀(即this.myFunc)后,Mock正常工作。
对应的代码示例:
index.js
const aws = require('aws-sdk') exports.handler = async function (event, context) { let s; switch (event.func) { case "myFunc": console.log('Executing myFunc'); // 直接调用:Mock不生效 s = await myFunc.apply(null, [event.params]) // 加this调用:Mock生效 // s = await this.myFunc.apply(null, [event.params]) console.log(s); return s; /*其他case...*/ default: // 默认逻辑 } // handler内部定义的myFunc async myFunc({p1, p2}){ /* 业务逻辑 */ return x } exports.myFunc = myFunc }
index.spec.js
jest.mock('./index.js', () => { const allAutoMocked = jest.createMockFromModule('./index.js') const actual = jest.requireActual('./index.js') return { __esModules: true, ...allAutoMocked, myFunc : jest.fn().mockImplementation(() => ({ mockedValue: 'test' })), handler: actual.handler } }) const index = require("./index.js") describe('Test myFunc', () => { test('If myFunc function was called', async () => { const event = { func: 'myFunc', params: { p1: 'xx', p2: false } }; const context = {}; await index.handler(event, context); expect(index.myFunc).toHaveBeenCalled(); }) })
核心原因
1. 内部函数与模块导出的引用分离
myFunc是定义在handler函数内部的局部函数,之后exports.myFunc = myFunc只是把这个函数的引用赋值给了模块导出对象的属性。但handler内部直接调用myFunc时,访问的是自己作用域内的局部变量,和模块导出的myFunc是两个指向同一函数的独立引用。
当你用Jest Mock替换exports.myFunc时,只是修改了模块导出对象上的属性指向,并没有改变handler内部局部变量myFunc的指向——所以直接调用时,依然执行的是原函数,Mock不生效。
2. this的指向绑定
当你通过index.handler()调用handler时,由于handler是作为模块导出对象的方法(exports.handler)被调用,此时普通函数内部的this默认指向模块的exports对象。
所以this.myFunc实际访问的是exports.myFunc,也就是被Jest替换后的Mock函数,因此Mock能正常生效。
解决方法
方法一:保留现有结构,用this.myFunc调用
这是你已经发现的有效方法,确保调用的是模块导出的Mock版本。
方法二:调整代码结构,将myFunc移到handler外部
把myFunc改为模块级别的函数,这样handler内部调用的myFunc就是模块级变量,和exports.myFunc指向同一个引用,Mock替换后直接调用即可生效:
const aws = require('aws-sdk') // 模块级别定义myFunc async myFunc({p1, p2}){ /* 业务逻辑 */ return x } exports.myFunc = myFunc exports.handler = async function (event, context) { let s; switch (event.func) { case "myFunc": console.log('Executing myFunc'); // 直接调用即可触发Mock s = await myFunc.apply(null, [event.params]) console.log(s); return s; /*其他case...*/ default: // 默认逻辑 } }
内容的提问来源于stack exchange,提问作者Kenobi
相关产品推荐
相关产品推荐

