如何测试useEffect中Redux action的调用?测试失败排查
问题
我想要测试useEffect钩子中是否调用了Redux action,相关代码如下:
Root组件
export default ({ children, initialState = {} }) => { return ( <Provider store={createStore(reducers, initialState, applyMiddleware(thunk))}> {children} </Provider> ); };
被Root包裹的App组件
import { clearState } from '../actions'; const App= (props) => { useEffect(() => { props.clearState(); }, []); return ( <div> App </div> ); }; const mapStateToProps = () => { return {}; }; export default connect(mapStateToProps, { clearState })(App);
clearState action
export const clearState = () => { return async (dispatch) => { dispatch({ type: CLEAR_STATE, payload: {} }); }; };
我编写了如下测试代码,验证App组件挂载时clearState action是否被调用:
const mockStore = configureStore([]); describe('App component', () => { it('should call clearState action on component mount', () => { // Create a mock store const store = mockStore({}); // Create a spy for the clearState action const clearStateSpy = jest.spyOn({ clearState }, 'clearState'); // Mount the component with the Provider and store const wrapper = mount( <Root store={store}> <App/> </Root> ); // Expect the clearState action to have been called expect(clearStateSpy).toHaveBeenCalled(); }); });
但测试失败,显示该action的调用次数为0,请问我哪里出错了?
问题分析与解决办法
错误原因
你创建的spy完全没关联到App组件实际调用的clearState:
jest.spyOn({ clearState }, 'clearState')是把clearState放到一个临时对象里,然后spy这个临时对象的方法,但App组件里调用的是从../actions导入的原始clearState,和这个临时对象的方法没有任何关系,所以spy无法捕获调用。
解决办法
这里提供三种可行的测试方案:
方案一:Spy真实导入的action creator
直接从actions模块spy导出的clearState,确保捕获到组件里的调用:
import { clearState } from '../actions'; const mockStore = configureStore([]); describe('App component', () => { it('should call clearState action on component mount', () => { const store = mockStore({}); // Spy actions模块导出的clearState const clearStateSpy = jest.spyOn(require('../actions'), 'clearState'); mount( <Root store={store}> <App/> </Root> ); expect(clearStateSpy).toHaveBeenCalled(); // 测试完清理spy clearStateSpy.mockRestore(); }); });
方案二:通过mock store检查dispatch的action
因为clearState是thunk action,最终会dispatchCLEAR_STATE类型的action,所以可以直接检查store的action记录:
const mockStore = configureStore([]); describe('App component', () => { it('should dispatch CLEAR_STATE on mount', () => { const store = mockStore({}); mount( <Root store={store}> <App/> </Root> ); // 获取store中所有dispatch的action const actions = store.getActions(); // 检查是否有CLEAR_STATE类型的action expect(actions).toContainEqual({ type: 'CLEAR_STATE', payload: {} }); }); });
方案三:测试未连接的原始组件(推荐)
把App组件的未连接版本导出,测试时直接传入mock的clearState方法,这样不需要依赖Redux Provider,测试更独立:
首先修改App组件,导出未连接的版本:
import { clearState } from '../actions'; export const App= (props) => { // 导出原始组件 useEffect(() => { props.clearState(); }, []); return ( <div> App </div> ); }; const mapStateToProps = () => { return {}; }; export default connect(mapStateToProps, { clearState })(App);
然后编写测试:
import { App } from './App'; // 导入未连接的组件 describe('App component', () => { it('should call clearState on mount', () => { const mockClearState = jest.fn(); mount(<App clearState={mockClearState} />); expect(mockClearState).toHaveBeenCalled(); }); });
内容的提问来源于stack exchange,提问作者user10518298
相关产品推荐
相关产品推荐

