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

如何为修改状态数组的React函数编写单元测试?

为handleSelect函数编写单元测试的思路与示例

核心测试场景

先明确要覆盖的关键逻辑:

  • 未选中的图片,点击后ID被加入imagesToDelete数组
  • 已选中的图片,点击后ID从imagesToDelete数组中移除
  • 空列表状态下,点击图片能正确添加第一个ID
  • 移除唯一选中的ID后,列表回到空状态
  • 多图片选择时,操作单个图片不影响其他选中项

第一步:提取纯逻辑简化测试

把handleSelect里的状态更新逻辑抽成纯函数,脱离组件状态依赖,更容易测试:

// 提取纯逻辑:输入当前选中ID列表和目标图片ID,返回新的选中列表
const toggleImageSelection = (currentSelectedIds, imageId) => {
  if (!currentSelectedIds.includes(imageId)) {
    return [...currentSelectedIds, imageId];
  } else {
    return currentSelectedIds.filter(id => id !== imageId);
  }
};

// 组件中修改handleSelect调用这个纯函数(用函数式更新避免状态快照问题)
const handleSelect = (image) => {
  setImagesToDelete(prevState => toggleImageSelection(prevState, image.id));
};

纯逻辑测试用例(Jest)

test('添加未选中的图片ID', () => {
  const currentIds = ['img-1', 'img-2'];
  const newId = 'img-3';
  expect(toggleImageSelection(currentIds, newId)).toEqual(['img-1', 'img-2', 'img-3']);
});

test('移除已选中的图片ID', () => {
  const currentIds = ['img-1', 'img-2', 'img-3'];
  const removedId = 'img-2';
  expect(toggleImageSelection(currentIds, removedId)).toEqual(['img-1', 'img-3']);
});

test('空列表添加第一个ID', () => {
  expect(toggleImageSelection([], 'img-1')).toEqual(['img-1']);
});

test('移除唯一ID后列表为空', () => {
  expect(toggleImageSelection(['img-1'], 'img-1')).toEqual([]);
});

第二步:测试组件交互(React Testing Library)

除了逻辑测试,还要验证用户交互后的实际表现(比如选中后显示图标):

import { render, screen, fireEvent } from '@testing-library/react';
import ImageGallery from './ImageGallery'; // 替换为你的组件文件名

test('点击未选中图片,显示选中图标', () => {
  const testImages = [
    { id: 'img-1', image: 'test-1.jpg' },
    { id: 'img-2', image: 'test-2.jpg' }
  ];
  render(<ImageGallery images={testImages} />);

  // 获取图片容器并点击
  const firstImageContainer = screen.getByAltText('Placeholder').parentElement;
  fireEvent.click(firstImageContainer);

  // 验证选中图标出现(需给SelectIcon添加data-testid="select-icon")
  expect(screen.getByTestId('select-icon')).toBeInTheDocument();
});

test('点击已选中图片,隐藏选中图标', () => {
  const testImages = [{ id: 'img-1', image: 'test-1.jpg' }];
  render(<ImageGallery images={testImages} />);

  const imageContainer = screen.getByAltText('Placeholder').parentElement;
  // 第一次点击选中
  fireEvent.click(imageContainer);
  expect(screen.getByTestId('select-icon')).toBeInTheDocument();

  // 第二次点击取消选中
  fireEvent.click(imageContainer);
  expect(screen.queryByTestId('select-icon')).not.toBeInTheDocument();
});

测试要点提醒

  • 优先测试用户能看到的行为(图标显示/隐藏),而非直接断言内部状态,这更贴近真实用户操作场景
  • 使用函数式更新setImagesToDelete(prev => ...),避免状态快照导致的逻辑错误,同时让测试更可靠
  • 如果需要直接验证状态变化,可以用renderHook测试封装后的状态逻辑钩子

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 21:55:08