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

如何用Jest测试Redux组件挂载时dispatch属性是否被调用?

问题:验证React Redux组件渲染时是否调用API请求的dispatch方法

需要编写Jest测试,验证React Redux组件在渲染时是否调用了用于API请求的dispatch方法fetchData。组件代码如下:

import * as React from 'react';
import { connect } from 'react-redux';
import { bindActions } from 'reports/utils/redux';

const connectedComponent = ({ foo, fetchData }) => {
    const loadData = React.useCallback(async () => {
        if (foo) {
            await fetchData(foo.id);
        }
    }, []);
    
    React.useEffect(() => { loadData(); }, []);
    return <></>;
}

const mapStateToProps = (state) => {
    const foo = <从Redux store中获取foo>
    return { foo };
};

const mapDispatchToProps = bindActions({
    fetchData: (fooId) => api.get({ foo_id: fooId }),
});

export default connect(mapStateToProps, mapDispatchToProps)(connectedComponent);

测试时尝试过mock react-redux的connect函数,也尝试过将fetchData作为state传入mockStore,但都无效,测试代码如下:

...

const middlewares = [ReduxThunk];
const mockStore = configureStore(middlewares);

describe('Connected Component', () => {
    it('should load team usage and limits on mount', async () => {
        let fetchDataMock = jest.fn();
        const store = mockStore({ user: { team_id: 1 }, fetchData: fetchDataMock });
        await act(async () => {
            render(
                <Provider store={store}>
                    <connectedComponent />
                </Provider>,
            );
        });

        console.log(store);
        const actions = store.getActions();
        console.log(actions); // [] 此处为空

        expect(fetchDataMock).toHaveBeenCalledTimes(1);
        expect(fetchDataMock).toHaveBeenCalledWith({ team_id: 1 });
    });
});

出现错误:Actions must be plain objects. Use custom middleware for async actions.(翻译:动作必须是普通对象。请为异步动作使用自定义中间件。)


错误原因分析

  1. dispatch函数放错位置:fetchData是通过mapDispatchToProps注入的组件props,并非store的state属性,直接加到mockStore的state里不会被组件识别。
  2. 异步动作未正确处理:fetchData返回异步函数(因api.get是异步操作),若bindActions未用thunk包装,或mockStore未正确配置thunk中间件,就会触发"动作必须是普通对象"的错误。

解决方案

方法1:直接测试未连接的原始组件(推荐)

跳过Redux连接逻辑,直接测试纯组件connectedComponent,手动传入所需props,聚焦组件本身的业务逻辑:

import connectedComponent from './your-component-path';

describe('Connected Component', () => {
    it('调用fetchData当foo存在时', async () => {
        const fetchDataMock = jest.fn().mockResolvedValue({});
        const foo = { id: 1 };

        await act(async () => {
            render(<connectedComponent foo={foo} fetchData={fetchDataMock} />);
        });

        expect(fetchDataMock).toHaveBeenCalledTimes(1);
        expect(fetchDataMock).toHaveBeenCalledWith(1);
    });

    it('不调用fetchData当foo不存在时', async () => {
        const fetchDataMock = jest.fn();

        await act(async () => {
            render(<connectedComponent foo={null} fetchData={fetchDataMock} />);
        });

        expect(fetchDataMock).not.toHaveBeenCalled();
    });
});

方法2:Mock bindActions工具函数

若要测试连接后的组件,可mockreports/utils/redux中的bindActions,让它返回我们的mock函数:

import { bindActions } from 'reports/utils/redux';
import ConnectedComponent from './your-component-path';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';

// Mock bindActions
jest.mock('reports/utils/redux', () => ({
    bindActions: jest.fn(() => ({
        fetchData: jest.fn().mockResolvedValue({})
    }))
}));

describe('Connected Component', () => {
    it('挂载时触发fetchData', async () => {
        const mockFetchData = jest.fn().mockResolvedValue({});
        (bindActions as jest.Mock).mockReturnValue({ fetchData: mockFetchData });

        // 创建包含foo的store
        const store = configureStore({
            reducer: () => ({
                foo: { id: 1 }
            })
        });

        await act(async () => {
            render(
                <Provider store={store}>
                    <ConnectedComponent />
                </Provider>
            );
        });

        expect(mockFetchData).toHaveBeenCalledTimes(1);
        expect(mockFetchData).toHaveBeenCalledWith(1);
    });
});

方法3:修复mockStore配置

确保store返回正确的foo数据,同时mockapi.get接口,保证thunk中间件能处理异步动作:

import ConnectedComponent from './your-component-path';
import { Provider } from 'react-redux';
import { configureStore } from '@reduxjs/toolkit';
import thunk from 'redux-thunk';
import * as api from './your-api-path';

// Mock api.get
jest.mock('./your-api-path', () => ({
    get: jest.fn().mockResolvedValue({})
}));

describe('Connected Component', () => {
    it('挂载时调用api.get并传入foo.id', async () => {
        const store = configureStore({
            reducer: () => ({
                foo: { id: 1 }
            }),
            middleware: [thunk]
        });

        await act(async () => {
            render(
                <Provider store={store}>
                    <ConnectedComponent />
                </Provider>
            );
        });

        expect(api.get).toHaveBeenCalledTimes(1);
        expect(api.get).toHaveBeenCalledWith({ foo_id: 1 });
    });
});

内容的提问来源于stack exchange,提问作者Jonathan Lee

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 00:06:04