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

如何对使用Redux selectors的React组件进行单元测试

解决方案

核心思路

你的组件渲染逻辑完全依赖Redux中存储的view值,测试时只要手动控制这个状态的返回值,分别校验两种场景下对应组件是否渲染即可。

步骤1:给子组件添加测试标识

先给两个视图组件加测试ID,方便测试时定位:

// Projects组件返回值部分修改
return (
  <Wrapper>
    <div className="projects-toolbar" data-testid="projects-toolbar-container">
      <Toolbar />
    </div>
    {view === VIEW_TYPES.grid && <ProjectsGridView data-testid="projects-grid-view" />}
    {view === VIEW_TYPES.list && <ProjectsListView data-testid="projects-list-view" />}
  </Wrapper>
);

步骤2:补充测试用例

你可以选择两种方案实现状态控制:

方案1:轻量mock useSelector(推荐)

直接mock Redux的hook返回值,不需要启动完整store:

// 测试文件顶部加mock
import { useSelector, useDispatch } from 'react-redux';
import { VIEW_TYPES } from '你的常量文件路径';
import { selectView } from '你的selector路径';
import { projectsActions } from '你的action路径';

jest.mock('react-redux', () => ({
  ...jest.requireActual('react-redux'),
  useSelector: jest.fn(),
  useDispatch: jest.fn(),
}));

const mockDispatch = jest.fn();

describe('Projects组件', () => {
  beforeEach(() => {
    useDispatch.mockReturnValue(mockDispatch);
    mockDispatch.mockClear();
  });

  test('renders toolbar container', () => {
    // 随便给一个view值不影响当前测试
    useSelector.mockImplementation(selector => 
      selector === selectView ? VIEW_TYPES.grid : undefined
    );
    const { getByTestId } = setup();
    expect(getByTestId('projects-toolbar-container')).toBeInTheDocument();
  });

  test('renders grid view when view type is grid', () => {
    useSelector.mockImplementation(selector => 
      selector === selectView ? VIEW_TYPES.grid : undefined
    );
    const { getByTestId, queryByTestId } = setup();
    expect(getByTestId('projects-grid-view')).toBeInTheDocument();
    // 同时校验列表视图不渲染
    expect(queryByTestId('projects-list-view')).not.toBeInTheDocument();
  });

  test('renders list view when view type is list', () => {
    useSelector.mockImplementation(selector => 
      selector === selectView ? VIEW_TYPES.list : undefined
    );
    const { getByTestId, queryByTestId } = setup();
    expect(getByTestId('projects-list-view')).toBeInTheDocument();
    // 同时校验网格视图不渲染
    expect(queryByTestId('projects-grid-view')).not.toBeInTheDocument();
  });

  // 额外可以测挂载时的dispatch调用是否符合预期
  test('calls restore actions on mount', () => {
    useSelector.mockImplementation(selector => 
      selector === selectView ? VIEW_TYPES.grid : undefined
    );
    setup();
    expect(mockDispatch).toHaveBeenCalledWith(projectsActions.viewRestore());
    expect(mockDispatch).toHaveBeenCalledWith(projectsActions.filterRestore());
  });
})

方案2:使用真实Redux Store测试

如果你的测试框架已经封装了带Redux Provider的render方法,可以直接传入初始state:

// 假设你的setup函数支持传入initialState参数
test('renders grid view when view type is grid', () => {
  const { getByTestId, queryByTestId } = setup({
    initialState: {
      // 对应你的Redux state结构,设置view为grid
      projects: { view: VIEW_TYPES.grid }
    }
  });
  expect(getByTestId('projects-grid-view')).toBeInTheDocument();
  expect(queryByTestId('projects-list-view')).not.toBeInTheDocument();
});

注意事项

  • 不要用getByTestId校验不存在的元素,会直接抛出错误,校验不存在的元素用queryByTestId
  • 如果子组件本身有依赖,测试时可以按需mock子组件,避免子组件内部逻辑影响当前测试

内容的提问来源于stack exchange,提问作者I'm not human

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 08:24:04