Jest如何测试React应用渲染前执行的函数调用
入口文件顶层调用函数的Jest测试方案
入口文件的顶层逻辑会在模块被首次导入时立刻执行,只要做好依赖Mock和模块状态重置,就可以精准验证breakWhenInternetExplorer的调用行为,具体实现步骤如下:
前置依赖Mock
测试前先把所有和目标逻辑无关的依赖全部替换为Mock实现,避免真实渲染应用带来的额外开销和无关报错:- 用
jest.mock()替换react-dom、App组件、AppProvider组件,拦截真实渲染逻辑 - 把待验证的
breakWhenInternetExplorer替换为Jest Spy函数,用来统计调用情况 - 提前在DOM中注入id为
root的容器节点,避免ReactDOM.render找不到挂载点报错
- 用
测试状态隔离
因为入口模块的顶层代码只会在首次导入时执行一次,每次测试执行前必须重置模块注册表和Mock调用记录,避免不同用例之间的状态污染:- 在
beforeEach钩子中执行jest.resetModules()清空已加载的模块缓存 - 重置所有Mock函数的调用记录
- 每次测试前重新创建root DOM节点,测试后移除节点
- 在
用例编写逻辑
不要在测试文件顶部静态导入入口文件,否则顶层代码会在Mock准备完成前提前执行。要在测试块内通过动态import()的方式加载入口模块,模块加载完成后立刻做断言:- 验证
breakWhenInternetExplorer确实被调用过 - (可选)验证调用时机早于
ReactDOM.render执行
- 验证
完整测试代码参考:
describe('入口文件逻辑测试', () => { let ReactDOM; let breakWhenInternetExplorer; beforeEach(() => { // 重置模块缓存 jest.resetModules(); // Mock所有依赖 jest.mock('react-dom', () => ({ render: jest.fn(), })); jest.mock('./App', () => jest.fn(() => null)); jest.mock('./components/AppProvider', () => ({ __esModule: true, default: jest.fn(({ children }) => children), })); jest.mock('./js/breakWhenInternetExplorer', () => ({ __esModule: true, default: jest.fn(), })); // 注入root挂载节点 const root = document.createElement('div'); root.id = 'root'; document.body.appendChild(root); // 拿到Mock后的依赖引用 ReactDOM = require('react-dom'); breakWhenInternetExplorer = require('./js/breakWhenInternetExplorer').default; }); afterEach(() => { // 清理DOM document.body.innerHTML = ''; }); test('初始化时会优先调用breakWhenInternetExplorer方法', async () => { // 标记调用顺序 let callOrder = []; breakWhenInternetExplorer.mockImplementation(() => { callOrder.push('breakIE'); }); ReactDOM.render.mockImplementation(() => { callOrder.push('render'); }); // 动态导入入口文件,触发顶层逻辑执行 await import('./index'); // 验证函数被调用 expect(breakWhenInternetExplorer).toHaveBeenCalledTimes(1); // 验证调用顺序在render之前 expect(callOrder).toEqual(['breakIE', 'render']); // 验证render接收到的参数正确(可选) expect(ReactDOM.render).toHaveBeenCalledTimes(1); expect(document.getElementById('root')).not.toBeNull(); }); });
注意:如果你的项目用了TypeScript或者开启了ESModule严格模式,动态导入时注意补充对应的类型声明即可,核心逻辑不需要调整。
内容的提问来源于stack exchange,提问作者Kiszuriwalilibori
相关产品推荐
相关产品推荐

