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)