You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何测试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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 05:37:42