DEV Community

Cover image for Testando Componentes com React Hook Form + Zod
Vitor Rios
Vitor Rios

Posted on

Testando Componentes com React Hook Form + Zod

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>
  );
}
Enter fullscreen mode Exit fullscreen mode

🧪 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',
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

✅ 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, ou getByRole, e evite getByTestId 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)