如何使用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
相关产品推荐
相关产品推荐

