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

如何为使用react-redux和react-router的Trade组件编写可被覆盖率统计的单元测试

Trade组件单元测试解决方案

问题原因

你当前的测试执行失败导致覆盖率未统计,根本原因是Trade组件依赖react-redux的useDispatch/useSelector、react-router-dom的useLocation三个钩子,直接渲染组件时缺少对应的上下文环境,测试抛出异常终止执行。

正确测试代码示例

我们采用钩子Mock的方式做单元测试,只验证Trade组件本身的逻辑,不耦合Redux、Router和子组件的实现:

import { render, screen } from '@testing-library/react';
import Trade from './trade';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import tradeActions from './trade-actions/trade-actions';
import appActions from '../app/actions/actions';

// Mock所有外部依赖钩子
jest.mock('react-redux', () => ({
  useDispatch: jest.fn(),
  useSelector: jest.fn(),
}));
jest.mock('react-router-dom', () => ({
  ...jest.requireActual('react-router-dom'),
  useLocation: jest.fn(),
}));
// Mock子组件,避免子组件依赖影响测试
jest.mock('./TradeFilters/TradeFilters', () => () => <div data-testid="trade-filters" />);
jest.mock('./TradeDataSeries/TradeDataSeries', () => ({ dataSource }) => <div data-testid="data-series" data-source={JSON.stringify(dataSource)} />);
jest.mock('../../components/SelectedData/SelectedData', () => ({ facts }) => <div data-testid="selected-data" data-facts={JSON.stringify(facts)} />);
// Mock action 避免真实副作用
jest.mock('./trade-actions/trade-actions', () => ({
  fetchTradeDataseriesRequest: jest.fn(),
  fetchDatasourceDocumentsRequest: jest.fn(),
  fetchCountriesRequest: jest.fn(),
  fetchProductsRequest: jest.fn(),
  fetchBorderPointsRequest: jest.fn(),
  setSelectedDataSeries: jest.fn(),
}));
jest.mock('../app/actions/actions', () => ({
  setDomain: jest.fn(),
}));

describe('Trade Component Test', () => {
  const mockDispatch = jest.fn();
  const mockLocation = { pathname: '/trade' };

  beforeEach(() => {
    // 每次用例执行前重置所有mock状态
    jest.clearAllMocks();
    useDispatch.mockReturnValue(mockDispatch);
    useLocation.mockReturnValue(mockLocation);
  });

  it('基础渲染快照匹配', () => {
    // 给useSelector设置返回值
    useSelector.mockImplementation((selector) => selector({
      trade: {
        dataseries: [],
        facts: { total: 100, amount: 2000 }
      }
    }));

    const { asFragment } = render(<Trade />);
    expect(asFragment()).toMatchSnapshot();
    // 验证子组件都渲染
    expect(screen.getByTestId('trade-filters')).toBeInTheDocument();
    expect(screen.getByTestId('data-series')).toBeInTheDocument();
    expect(screen.getByTestId('selected-data')).toBeInTheDocument();
  });

  it('dataseries为空时挂载触发所有初始化请求', async () => {
    useSelector.mockImplementation((selector) => selector({
      trade: {
        dataseries: [],
        facts: {}
      }
    }));

    render(<Trade />);

    // 验证初始化useEffect的dispatch
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.fetchTradeDataseriesRequest());
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.fetchDatasourceDocumentsRequest());
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.fetchCountriesRequest());
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.fetchProductsRequest());
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.fetchBorderPointsRequest());
    expect(mockDispatch).toHaveBeenCalledWith(appActions.setDomain('trade'));
    // 验证第二个useEffect的dispatch
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.setSelectedDataSeries([]));
    expect(mockDispatch).toHaveBeenCalledTimes(7);
  });

  it('dataseries不为空时挂载不重复触发数据请求', () => {
    useSelector.mockImplementation((selector) => selector({
      trade: {
        dataseries: [{ id: 1, name: '测试数据' }],
        facts: {}
      }
    }));

    render(<Trade />);

    // 只会触发第二个useEffect的清空选择的请求
    expect(mockDispatch).toHaveBeenCalledWith(tradeActions.setSelectedDataSeries([]));
    expect(mockDispatch).toHaveBeenCalledTimes(1);
    // 不会触发初始化数据请求
    expect(tradeActions.fetchTradeDataseriesRequest).not.toHaveBeenCalled();
  });
});

配套执行命令

你可以继续用原来的覆盖率命令验证:
npm test -- --coverage --watchAll=false

同类组件测试通用思路

  1. 先Mock所有组件依赖的第三方钩子(redux、router、国际化等)
  2. Mock所有子组件,避免子组件的依赖影响当前组件测试
  3. 给钩子设置对应返回值模拟不同场景
  4. 验证渲染结果、副作用执行逻辑符合预期

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 08:06:06