使用Jest & Enzyme测试组件的componentDidMount与onCreateUserSuccess方法
解决React组件Prop函数测试的问题
我来帮你搞定这个测试问题!你碰到的spyOn can only be applied to functions错误,还有模拟函数后测试失败的情况,大概率是因为没正确传递模拟函数作为props,或者测试时的渲染/调用时机没处理好。下面一步步给你梳理解决方案:
先理清核心问题
你的组件依赖必填的listUsers函数props,测试时必须传入一个可被监听的模拟函数——如果传的不是Jest(或你用的测试库)创建的mock函数,就会触发那个spyOn的错误。另外测试componentDidMount和onCreateUserSuccess时,要确保组件正确挂载、方法正确调用。
解决方案分步走
1. 正确创建模拟函数
首先用Jest创建mock函数来替代listUsers、notify这些props,还有组件内部的closeCreateUserModal方法(如果需要测试它的调用):
const mockListUsers = jest.fn(); const mockNotify = jest.fn(); const mockCloseModal = jest.fn();
2. 测试componentDidMount中listUsers仅调用一次
这里要注意:如果用Enzyme,得用mount而不是shallow(shallow渲染可能不会触发componentDidMount);如果用React Testing Library,直接render就行。
用React Testing Library的写法:
import React from 'react'; import { render } from '@testing-library/react'; import YourComponent from './YourComponent'; describe('YourComponent', () => { it('calls listUsers exactly once when component mounts', () => { const mockListUsers = jest.fn(); // 渲染组件时传入mock的listUsers render( <YourComponent securityMode="test-mode" listUsers={mockListUsers} notify={jest.fn()} /> ); // 断言mock函数只被调用一次 expect(mockListUsers).toHaveBeenCalledTimes(1); }); });
用Enzyme的写法:
import { mount } from 'enzyme'; import YourComponent from './YourComponent'; it('calls listUsers exactly once on componentDidMount', () => { const mockListUsers = jest.fn(); const wrapper = mount( <YourComponent securityMode="test-mode" listUsers={mockListUsers} notify={jest.fn()} /> ); expect(mockListUsers).toHaveBeenCalledTimes(1); });
3. 测试onCreateUserSuccess方法
这个方法要测试四个关键点:关闭弹窗的state变化、调用listUsers、调用closeCreateUserModal、调用notify并传入正确参数。写法如下:
it('behaves correctly when onCreateUserSuccess is called', () => { const mockListUsers = jest.fn(); const mockNotify = jest.fn(); const mockCloseCreateUserModal = jest.fn(); // 挂载组件,传入mock的props const wrapper = mount( <YourComponent securityMode="test-mode" listUsers={mockListUsers} notify={mockNotify} /> ); // 把组件内部的closeCreateUserModal替换成mock函数 wrapper.instance().closeCreateUserModal = mockCloseCreateUserModal; // 模拟接口返回的成功数据 const mockResponse = { username: 'test-user', group: 'admin-group' }; // 直接调用组件实例的onCreateUserSuccess方法 wrapper.instance().onCreateUserSucess(mockResponse); // 逐个断言: // 1. 弹窗状态被设为关闭 expect(wrapper.state('isCreateUserModalOpen')).toBe(false); // 2. listUsers被调用 expect(mockListUsers).toHaveBeenCalled(); // 3. closeCreateUserModal被调用 expect(mockCloseCreateUserModal).toHaveBeenCalled(); // 4. notify被传入正确的参数 expect(mockNotify).toHaveBeenCalledWith({ title: 'Created User', message: `User ${mockResponse.username} was added to group: ${mockResponse.group}`, status: STATUS.success, dismissible: true, dismissAfter: 3000 }); });
常见错误排查
spyOn can only be applied to functions:如果你之前尝试用jest.spyOn(this.props, 'listUsers'),但this.props.listUsers不是一个mock函数(比如没正确传入),就会报这个错。确保所有函数props都用jest.fn()创建的mock。listUsers没被调用:如果用Enzyme的shallow渲染,componentDidMount可能不会触发,换成mount就行;如果是Testing Library,确保组件已经完成渲染(同步方法不需要额外等待)。- 方法调用断言失败:如果
onCreateUserSuccess是异步触发的(比如从接口回调),需要用waitFor或async/await等待,但你的例子里是同步方法,直接调用实例方法就没问题。
总结
核心就是这几点:
- 所有必填的函数props都要传入测试库创建的mock函数
- 根据测试场景选对渲染方式(mount/shallow/render)
- 测试组件方法时,直接调用实例方法,然后断言对应的副作用(state变化、props函数调用、内部方法调用)
内容的提问来源于stack exchange,提问作者user10104341
相关产品推荐
相关产品推荐

