A combinação entre React Hook Form (RHF) e Zod tem se tornado cada vez mais comum em projetos modernos. Ela proporciona uma forma simples, rápida e tipada de lidar com formulários e validações — mas como garantir que tudo isso está funcionando corretamente?
Neste artigo, vamos aprender como testar formulários que usam RHF + Zod, cobrindo:
- Validação de campos
- Submissão bem-sucedida
- Mensagens de erro
- Testes automatizados com Jest + Testing Library
🎯 O que vamos testar?
Vamos usar como base um formulário simples de login:
// LoginForm.tsx import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; const schema = z.object({ email: z.string().email(), password: z.string().min(6), }); type FormData = z.infer<typeof schema>; export function LoginForm({ onSubmit }: { onSubmit: (data: FormData) => void }) { const { register, handleSubmit, formState: { errors }, } = useForm<FormData>({ resolver: zodResolver(schema), }); return ( <form onSubmit={handleSubmit(onSubmit)}> <input placeholder="Email" {...register('email')} /> {errors.email && <span>{errors.email.message}</span>} <input type="password" placeholder="Password" {...register('password')} /> {errors.password && <span>{errors.password.message}</span>} <button type="submit">Login</button> </form> ); }
🧪 Testes com @testing-library/react
Agora, vamos aos testes:
// LoginForm.test.tsx import { render, screen, fireEvent } from '@testing-library/react'; import { LoginForm } from './LoginForm'; describe('LoginForm', () => { it('shows validation errors when submitting empty form', async () => { const handleSubmit = jest.fn(); render(<LoginForm onSubmit={handleSubmit} />); fireEvent.click(screen.getByRole('button', { name: /login/i })); expect(await screen.findByText(/invalid email/i)).toBeInTheDocument(); expect(await screen.findByText(/at least 6 characters/i)).toBeInTheDocument(); expect(handleSubmit).not.toHaveBeenCalled(); }); it('submits successfully with valid data', async () => { const handleSubmit = jest.fn(); render(<LoginForm onSubmit={handleSubmit} />); fireEvent.input(screen.getByPlaceholderText(/email/i), { target: { value: 'test@example.com' }, }); fireEvent.input(screen.getByPlaceholderText(/password/i), { target: { value: '123456' }, }); fireEvent.click(screen.getByRole('button', { name: /login/i })); await screen.findByRole('button'); // espera render do submit expect(handleSubmit).toHaveBeenCalledWith({ email: 'test@example.com', password: '123456', }); }); });
✅ O que estamos validando aqui?
- Que os erros de validação aparecem corretamente ao submeter o formulário vazio
- Que a função
onSubmit
só é chamada quando os dados são válidos - Que o componente está de fato validando com base no schema Zod
🧠 Dicas extras
- Para campos dinâmicos (como arrays ou steps), use
useFieldArray
+ testes com múltiplos steps. - Utilize
renderWithProviders()
se seu formulário estiver dentro de Providers como Theme ou QueryClient. - Teste os campos com
screen.getByPlaceholderText
,getByLabelText
, ougetByRole
, e evitegetByTestId
a menos que não tenha alternativa.
🏁 Conclusão
Testar formulários com React Hook Form e Zod é simples quando você entende como os erros e validações são exibidos. Usar @testing-library/react
junto com mocks como jest.fn()
torna tudo mais previsível e confiável — e ainda te protege contra regressões no futuro.
Aposte nesse combo em seus projetos e garanta que os formulários estejam funcionando como esperado.
Top comments (0)