如何Mock React Hook Form中Controller的render属性以覆盖测试
解决React Hook Form Controller组件render属性测试覆盖问题
问题场景
使用React Testing Library测试自定义组件时,发现React Hook Form中Controller组件的render属性内的代码未被测试覆盖,需要补充测试逻辑覆盖这部分代码。
组件代码(InputComponent)
<div> <label>{label}</label> <Controller control={control} name={name} rules={validate} render={({ field: { onChange, value, name: iName } }) => ( <select name={iName} onChange={(e) => { onChange(e.target.value); action.handleInputValidation(e); }} onFocus={(e) => action.handleOnfocus(e)} value={value} > <option value="">{placeholder}</option> {data.map((option) => ( <option key={`option-${option.value}`} value={option.value} > {option.label} </option> ))} </select> )} /> </div>
初始测试文件
describe('Component', () => { let wrapper; beforeEach(() => { wrapper = render( <InputComponent name={name} placeholder={placeholder} label={label} data={dataSchema} controller={Controller} register={jest.fn()} /> ); }); test('InputComponent should be rendered', () => { // run expects }); });
解决方案
1. 正确配置React Hook Form测试环境
Controller依赖于React Hook Form的control对象,测试时需要通过useForm创建合法的control实例,而非直接传入Controller组件。同时要mock组件依赖的action对象,以便验证回调是否触发。
import { render, screen, fireEvent } from '@testing-library/react'; import { useForm } from 'react-hook-form'; import InputComponent from './InputComponent'; // Mock action对象 const mockAction = { handleInputValidation: jest.fn(), handleOnfocus: jest.fn() }; describe('InputComponent', () => { let control; const testProps = { name: 'test-select', placeholder: '请选择', label: '测试选择器', data: [ { value: '1', label: '选项1' }, { value: '2', label: '选项2' } ], action: mockAction // 传入mock的action }; beforeEach(() => { // 创建React Hook Form的control实例 const { control: formControl } = useForm({ defaultValues: { [testProps.name]: '' } }); control = formControl; render( <InputComponent {...testProps} control={control} // 传入合法的control /> ); });
2. 测试render内的UI渲染
验证render函数生成的select元素、选项是否正确渲染:
test('should render select element with correct options', () => { // 验证标签存在 expect(screen.getByLabelText(testProps.label)).toBeInTheDocument(); // 验证select元素存在 const selectElement = screen.getByRole('combobox'); expect(selectElement).toBeInTheDocument(); // 验证默认选项和自定义选项存在 expect(screen.getByText(testProps.placeholder)).toBeInTheDocument(); expect(screen.getByText('选项1')).toBeInTheDocument(); expect(screen.getByText('选项2')).toBeInTheDocument(); });
3. 测试render内的交互逻辑
覆盖onChange、onFocus等回调,验证触发逻辑是否正常:
test('should trigger handleOnfocus when select is focused', () => { const selectElement = screen.getByRole('combobox'); fireEvent.focus(selectElement); // 验证handleOnfocus被调用 expect(mockAction.handleOnfocus).toHaveBeenCalledTimes(1); expect(mockAction.handleOnfocus).toHaveBeenCalledWith(expect.any(Object)); }); test('should trigger onChange and handleInputValidation when selecting an option', () => { const selectElement = screen.getByRole('combobox'); // 选择选项1 fireEvent.change(selectElement, { target: { value: '1' } }); // 验证handleInputValidation被调用 expect(mockAction.handleInputValidation).toHaveBeenCalledTimes(1); expect(mockAction.handleInputValidation).toHaveBeenCalledWith(expect.any(Object)); });
4. (可选)Mock Controller组件(不推荐)
如果需要单独测试render函数逻辑,可mockController组件,让它直接执行传入的render函数:
import { Controller } from 'react-hook-form'; // Mock Controller,直接返回render函数的结果 jest.mock('react-hook-form', () => ({ ...jest.requireActual('react-hook-form'), Controller: ({ render }) => render({ field: { onChange: jest.fn(), value: '', name: 'test-select' } }) })); // 之后的测试可以直接验证render生成的元素和交互
注意:React Testing Library的理念是测试组件的实际行为而非实现细节,因此优先推荐通过测试UI交互来覆盖
render内的代码,而非mockController。
内容的提问来源于stack exchange,提问作者vfzapbed
相关产品推荐
相关产品推荐

