为何Jest Spy在被测文件解构导入被监视函数时失效?
为什么Jest的Spy无法监视解构导入的函数?
问题场景
测试文件代码:
const webhook = require('../../../../../../src/lib/apis/webhook'); const internalFunc = jest.spyOn(webhook, 'handleWebhookCreation').mockImplementation(() => { return expectedReturn; });
被测文件原始代码:
const { handleWebhookCreation } = require('../../../apis/webhook'); const createWebhook = async (req, res) => { for (const webhook of payload) { results.push(await handleWebhookCreation(webhook, t)); } };
上述代码无法正常工作,调用的是原函数而非被模拟/监视的版本。
解决方法
修改被测文件,不使用解构导入,而是导入整个模块对象,再调用其中的方法:
const _webhook = require('../../../apis/webhook'); // ... results.push(await _webhook.handleWebhookCreation(webhook, t));
此时单元测试中的Spy/模拟可以正常工作。
原因解释
这本质是模块导出的引用机制和解构赋值的行为共同导致的:
- 当你用
const { handleWebhookCreation } = require('xxx')解构导入时,相当于把模块导出对象里handleWebhookCreation属性对应的原始函数引用,复制到了当前文件的handleWebhookCreation变量中。这个变量和原导出对象的属性彻底解绑,后续导出对象的属性变化不会影响它。 - Jest的
spyOn方法是通过修改模块导出对象上的目标属性,把原函数替换成模拟函数。但被测文件里的handleWebhookCreation变量依然指向最初复制的原始函数,所以调用时不会触发模拟逻辑。 - 改成通过模块对象调用
_webhook.handleWebhookCreation时,每次都会从模块导出对象上读取该属性,这时候读到的就是被Jest替换后的模拟函数,因此能正常生效。
内容的提问来源于stack exchange,提问作者Tanner Summers
相关产品推荐
相关产品推荐

