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

如何使用Jest/React Testing Library测试带onClick的React组件

测试实现方案

你当前写的const btn = screen.getByRole('button', {name: '1 hour'});放在测试用例外部,会在组件还未渲染时就执行DOM查询,直接导致查询失败,所有DOM查询操作都需要放在组件渲染完成后的用例内部执行。

你要覆盖onClick的两行逻辑,只需要分别验证回调函数执行情况、激活状态更新结果即可,不需要直接mock组件内部的setActiveIdx:

  • 回调函数inner.cb通过jest模拟即可验证执行次数和传参
  • 状态更新的结果可以通过按钮激活后的UI特征(比如激活类名、aria属性等)验证

1. 前置准备

首先构造带mock回调的测试用数据:

// 用jest.fn生成模拟回调函数
const mockCallback = jest.fn();
// 测试用数据源,和你实际业务数据结构保持一致即可
const TestData = [
  {
    id: 1,
    title: '1 hour',
    cb: mockCallback
  },
  {
    id: 2,
    title: '2 hour',
    cb: mockCallback
  }
];

2. 完整测试用例

import { render, screen, fireEvent } from '@testing-library/react';
import ButtonGroup from './你的组件路径';

describe('ButtonGroup 组件', () => {
  // 每个测试用例执行前重置mock状态,避免用例之间互相影响
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('正常渲染不崩溃', () => {
    const { container } = render(<ButtonGroup data={TestData}/>);
    expect(container).toBeInTheDocument();
  });

  it('点击按钮后回调正确触发、激活状态正常更新', () => {
    render(<ButtonGroup data={TestData}/>);
    // 在组件渲染完成后再查询目标按钮
    const targetBtn = screen.getByRole('button', { name: '1 hour' });
    // 触发点击事件
    fireEvent.click(targetBtn);

    // 校验回调执行情况:执行1次,传参为当前按钮的index(第一个按钮index为0)
    expect(mockCallback).toBeCalledTimes(1);
    expect(mockCallback).toBeCalledWith(0);

    // 校验激活状态:替换为你组件实际的激活标识即可
    // 比如激活后有active类名,或者有aria-pressed="true"属性
    expect(targetBtn).toHaveClass('active');
  });
});

补充优化建议

你组件里的按钮data-testid存在语法错误,少了闭合引号,建议修改为带index区分每个按钮唯一的testid,方便测试查询:

<button 
  type="button" 
  data-testid={`btn-test-${index}`}
  onClick={() => {setActiveIdx(() => index); inner.cb(index)}}
  key={inner.id}
>
  {inner.title}
</button>

修改后你也可以通过testid查询按钮,不用依赖按钮文案:

const targetBtn = screen.getByTestId('btn-test-0');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 01:06:02