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

如何使用Jest测试react-hook-form V7 Controller API组件

Jest测试react-hook-form Controller组件的正确方法

完全支持对基于react-hook-form Controller API开发的组件编写Jest单元测试,你之前遇到渲染结果不符合预期的核心原因是Controller的mock写法错误:你将Controller直接mock为返回空Fragment的组件,其内部render prop对应的UI逻辑完全不会执行,自然无法渲染出ToggleButtonGroup等子内容。

推荐测试方案(不mock核心API)

不要mock react-hook-form的Controller、useForm等核心能力,直接使用真实的库逻辑构造测试环境,测试结果更贴近生产运行状态,可信度更高。根据组件获取control实例的方式分两种场景处理:

场景1:组件通过props接收control

如果你的自定义组件将control作为props传入,测试时通过useForm生成真实的control实例传入即可:

import { render, screen, fireEvent } from '@testing-library/react';
import { useForm } from 'react-hook-form';
import PaymentOptions from './PaymentOptions';

// 测试用包装组件,承载form实例
const FormWrapper = () => {
  const { control } = useForm({
    defaultValues: {
      test: 'one' // 与组件内defaultValue配置对齐
    }
  });
  return <PaymentOptions control={control} />;
};

describe('PaymentOptions component', () => {
  it('正确渲染所有支付方式单选按钮', () => {
    render(<FormWrapper />);
    // 断言所有选项正常渲染
    expect(screen.getByText('billet')).toBeInTheDocument();
    expect(screen.getByText('pix')).toBeInTheDocument();
    expect(screen.getByText('credit card')).toBeInTheDocument();
  });

  it('点击选项可以正常切换选中状态', () => {
    render(<FormWrapper />);
    const pixOption = screen.getByText('pix');
    fireEvent.click(pixOption);
    // 根据组件实际UI表现断言选中状态,比如校验active类名、aria-checked属性
    expect(pixOption).toHaveClass('active');
  });
});

场景2:组件通过useFormContext获取control

如果你的组件嵌套在FormProvider内部,依赖上下文获取control实例,测试时直接用FormProvider包裹组件即可:

import { render, screen, fireEvent } from '@testing-library/react';
import { useForm, FormProvider } from 'react-hook-form';
import PaymentOptions from './PaymentOptions';

describe('PaymentOptions component', () => {
  it('正确渲染所有支付方式单选按钮', () => {
    const methods = useForm({ defaultValues: { test: 'one' } });
    render(
      <FormProvider {...methods}>
        <PaymentOptions />
      </FormProvider>
    );

    // 渲染断言、交互断言逻辑和场景1完全一致
    expect(screen.getByText('billet')).toBeInTheDocument();
  });
});

仅做快照测试时的Controller正确mock写法

如果只是需要生成纯渲染快照、不需要验证交互逻辑,mock Controller时必须执行传入的render函数,返回对应UI,不能直接返回空节点:

jest.mock('react-hook-form', () => {
  const originalLib = jest.requireActual('react-hook-form');
  return {
    ...originalLib,
    Controller: ({ render }) => render({
      field: {
        onChange: jest.fn(),
        onBlur: jest.fn(),
        value: 'one',
        name: 'test',
        ref: jest.fn()
      },
      fieldState: { error: undefined }
    }),
    useFormContext: () => ({
      control: {}
    })
  }
});

注意:这种mock方式绕过了react-hook-form的真实状态逻辑,无法验证表单交互、值同步、校验联动等核心功能,仅适合纯UI快照场景,优先推荐使用真实form实例的测试方案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 17:09:30