如何使用Jest模拟ES6模块中的变量,使Redux Reducer基于模拟值完成场景测试
我明白你想要直接控制buttonReducer中那三个常量的值,从而单独测试每个case的逻辑——而不是通过模拟整个store来间接测试。之前的jest.mock没生效,大概率是因为你的mock让getActionTypeName返回固定值,导致三个常量都变成同一个值,没法区分不同的action type。下面给你两种可行的方案:
方案1:改进Jest Mock,让getActionTypeName根据参数返回对应常量
既然你的三个常量是通过getActionTypeName传入不同字符串生成的(注意你的示例代码里FETCH_TYPE和SHOW_TYPE都传了'RESET',这应该是笔误,建议改成'FETCH'和'SHOW'),我们可以让mock函数根据传入的参数返回指定的固定值,这样每个常量都会被赋予唯一的type:
// 测试文件中的mock代码 jest.mock('./functions', () => { return { getActionTypeName: (actionSuffix) => { // 根据传入的后缀返回对应的常量值 switch(actionSuffix) { case 'RESET': return 'BUTTON_RESET'; case 'FETCH': return 'BUTTON_FETCH'; case 'SHOW': return 'BUTTON_SHOW'; default: return actionSuffix; } } }; });
之后在测试用例中,你可以直接导入这三个常量,生成对应action来测试:
import { buttonReducer, RESET_TYPE, FETCH_TYPE, SHOW_TYPE } from './reducer'; test('handles RESET_TYPE action', () => { const initialState = { name: 'John' }; const newState = buttonReducer(initialState, { type: RESET_TYPE }); // 断言newState符合你的预期逻辑 expect(newState).toEqual(/* 你的预期状态 */); }); // 同理测试FETCH_TYPE和SHOW_TYPE的case
方案2:直接覆盖模块导出的常量值
如果你不想mockgetActionTypeName,而是想直接修改已经生成的常量,你可以通过Object.defineProperty来修改模块导出的const变量(因为const声明的变量在模块导出后,我们可以通过这种方式修改它的值):
import * as reducerModule from './reducer'; // 在每个测试前重置常量值 beforeEach(() => { Object.defineProperty(reducerModule, 'RESET_TYPE', { value: 'TEST_RESET' }); Object.defineProperty(reducerModule, 'FETCH_TYPE', { value: 'TEST_FETCH' }); Object.defineProperty(reducerModule, 'SHOW_TYPE', { value: 'TEST_SHOW' }); }); test('handles custom RESET_TYPE', () => { const action = { type: reducerModule.RESET_TYPE }; const result = reducerModule.buttonReducer(undefined, action); expect(result).toEqual(/* 预期状态 */); });
这种方式的好处是完全绕过了getActionTypeName函数,直接控制常量的值,适合你想要完全隔离测试reducer逻辑的场景。
最后提醒一下:你的示例代码中FETCH_TYPE和SHOW_TYPE都调用了getActionTypeName('RESET'),这会导致三个常量的值完全相同,reducer里的case会重复,没法区分不同的action类型,记得修正这个笔误哦。
内容的提问来源于stack exchange,提问作者Tarun

