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

如何用react-testing-library和react-hook-form测试无提示表单验证逻辑?

如何测试无错误UI的react-hook-form表单验证逻辑

针对你遇到的「验证失败时组件不渲染错误提示,无法通过UI断言测试验证逻辑」的问题,以下是几个实用的测试方案,以及关于是否mock useForm的建议:

方案1:测试提交行为的结果(推荐)

遵循React Testing Library「测试用户行为而非实现细节」的理念,通过验证提交是否触发预期回调来判断验证逻辑是否生效:

组件示例

import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import * as yup from 'yup';

const schema = yup.object({
  email: yup.string().email('无效邮箱').required('必填'),
});

export const LoginForm = ({ onSubmit }) => {
  const { register, handleSubmit } = useForm({
    resolver: yupResolver(schema),
    mode: 'onSubmit',
  });

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} placeholder="邮箱" />
      <button type="submit">登录</button>
    </form>
  );
};

测试代码

import { render, screen, fireEvent } from '@testing-library/react';
import { LoginForm } from './LoginForm';
import { vi } from 'vitest';

test('输入无效邮箱时,提交不触发回调', async () => {
  const mockSubmit = vi.fn();
  render(<LoginForm onSubmit={mockSubmit} />);

  // 输入无效值并提交
  fireEvent.change(screen.getByPlaceholderText('邮箱'), { target: { value: 'invalid-email' } });
  fireEvent.submit(screen.getByRole('form'));

  expect(mockSubmit).not.toHaveBeenCalled();
});

test('输入有效邮箱时,提交触发回调并传递正确数据', async () => {
  const mockSubmit = vi.fn();
  render(<LoginForm onSubmit={mockSubmit} />);

  fireEvent.change(screen.getByPlaceholderText('邮箱'), { target: { value: 'test@example.com' } });
  fireEvent.submit(screen.getByRole('form'));

  expect(mockSubmit).toHaveBeenCalledWith({ email: 'test@example.com' });
});

方案2:通过暴露Form实例验证内部状态

如果需要更细粒度的验证(比如检查具体字段的错误类型),可以在组件中添加一个回调props,将useForm返回的实例暴露给测试代码:

组件改造

export const LoginForm = ({ onSubmit, onFormReady }) => {
  const form = useForm({
    resolver: yupResolver(schema),
    mode: 'onSubmit',
  });
  const { register, handleSubmit } = form;

  // 暴露form实例给测试
  useEffect(() => {
    onFormReady?.(form);
  }, [form, onFormReady]);

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('email')} placeholder="邮箱" />
      <button type="submit">登录</button>
    </form>
  );
};

测试代码

test('邮箱为空时,form状态标记验证失败', async () => {
  let formInstance;
  render(<LoginForm onFormReady={(form) => (formInstance = form)} />);

  fireEvent.submit(screen.getByRole('form'));

  expect(formInstance.formState.isValid).toBe(false);
  expect(formInstance.formState.errors.email).toBeDefined();
  expect(formInstance.formState.errors.email.message).toBe('必填');
});

是否需要mock useForm?

不推荐。mock useForm会让测试脱离真实的验证逻辑,变成「测试mock是否正确」而非「测试表单验证是否生效」,容易出现测试通过但实际代码存在bug的情况。

只有当你的组件与react-hook-form耦合极深,且需要单独隔离测试某个非验证逻辑时,才考虑局部mock,但要确保mock的逻辑与真实useForm的行为一致。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 10:15:10