![]()
Forms are a critical part of many web applications, and ensuring that they behave as expected is vital for a good user experience. When writing test cases for forms in React, it’s important to test both the functionality and the UI to ensure that the form validates data, submits correctly, and responds to user interactions as expected.
This guide will help you write effective test cases for forms in React using React Testing Library and Jest.
1. Types of Form Testing
When testing forms in React, your tests should cover a variety of cases:
- Initial State: Ensure that the form renders with the correct initial state (e.g., empty inputs or default values).
- User Input: Test that user interactions (e.g., typing into fields, selecting options) are correctly handled.
- Validation: Verify that validation works as expected (both client-side and form submission errors).
- Submission: Test that the form submission behaves correctly (e.g., making API calls, showing success or error messages).
- Resetting or Clearing the Form: Ensure that the form behaves as expected when reset or cleared.
2. Setting Up the Form Component
Let’s assume we have a basic form with validation. The form includes:
- A
namefield. - An
emailfield. - A submit button.
Example Form Component
// UserForm.js
import React, { useState } from 'react';
const UserForm = ({ onSubmit }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const validateForm = () => {
if (!name || !email) {
setError('Both fields are required');
return false;
}
if (!/\S+@\S+\.\S+/.test(email)) {
setError('Invalid email format');
return false;
}
setError('');
return true;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validateForm()) {
onSubmit({ name, email });
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
{error && <p role="alert">{error}</p>}
<button type="submit">Submit</button>
</form>
);
};
export default UserForm;
3. Writing Test Cases for the Form
Let’s now write test cases for the above form using React Testing Library and Jest.
1. Test Initial Render
Make sure the form renders correctly with initial empty values.
import { render, screen } from '@testing-library/react';
import UserForm from './UserForm';
test('renders form with empty fields initially', () => {
render(<UserForm onSubmit={jest.fn()} />);
// Check that the name input and email input are empty initially
expect(screen.getByLabelText(/name/i).value).toBe('');
expect(screen.getByLabelText(/email/i).value).toBe('');
// Ensure there is no error message shown initially
expect(screen.queryByRole('alert')).toBeNull();
});
2. Test User Input
Test that when a user types in the form fields, the inputs are updated correctly.
test('updates input values on user typing', () => {
render(<UserForm onSubmit={jest.fn()} />);
const nameInput = screen.getByLabelText(/name/i);
const emailInput = screen.getByLabelText(/email/i);
// Simulate typing into the name input
fireEvent.change(nameInput, { target: { value: 'John Doe' } });
expect(nameInput.value).toBe('John Doe');
// Simulate typing into the email input
fireEvent.change(emailInput, { target: { value: 'john.doe@example.com' } });
expect(emailInput.value).toBe('john.doe@example.com');
});
3. Test Validation
Test that the form shows an error message when required fields are empty or the email format is incorrect.
test('shows an error message when fields are invalid', () => {
render(<UserForm onSubmit={jest.fn()} />);
// Simulate a submit with empty fields
fireEvent.click(screen.getByText(/submit/i));
// Assert that the error message is shown for required fields
expect(screen.getByRole('alert')).toHaveTextContent('Both fields are required');
// Simulate invalid email format
fireEvent.change(screen.getByLabelText(/name/i), { target: { value: 'John Doe' } });
fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'invalid-email' } });
fireEvent.click(screen.getByText(/submit/i));
// Assert that the error message for invalid email format appears
expect(screen.getByRole('alert')).toHaveTextContent('Invalid email format');
});
4. Test Form Submission
Test that the form submission works correctly when the fields are valid. You can mock the onSubmit function to verify the form data is passed correctly.
test('submits form data when valid', () => {
const mockSubmit = jest.fn();
render(<UserForm onSubmit={mockSubmit} />);
// Fill out the form
fireEvent.change(screen.getByLabelText(/name/i), { target: { value: 'John Doe' } });
fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'john.doe@example.com' } });
// Submit the form
fireEvent.click(screen.getByText(/submit/i));
// Assert that the onSubmit function was called with correct values
expect(mockSubmit).toHaveBeenCalledWith({
name: 'John Doe',
email: 'john.doe@example.com',
});
});
5. Test Error Message Clearing on Valid Input
Test that the error message is cleared when the user provides valid inputs after encountering an error.
test('clears error message on valid input', () => {
render(<UserForm onSubmit={jest.fn()} />);
// Simulate submitting with invalid data
fireEvent.click(screen.getByText(/submit/i));
expect(screen.getByRole('alert')).toHaveTextContent('Both fields are required');
// Fill out the form with valid data
fireEvent.change(screen.getByLabelText(/name/i), { target: { value: 'John Doe' } });
fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'john.doe@example.com' } });
fireEvent.click(screen.getByText(/submit/i));
// Assert that the error message is cleared after submitting valid data
expect(screen.queryByRole('alert')).toBeNull();
});
6. Test Form Reset (if applicable)
If your form has a reset button to clear the form, you should test that it works as expected.
test('resets form fields when reset button is clicked', () => {
render(<UserForm onSubmit={jest.fn()} />);
// Fill out the form
fireEvent.change(screen.getByLabelText(/name/i), { target: { value: 'John Doe' } });
fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'john.doe@example.com' } });
// Simulate form reset (if reset button is present)
fireEvent.click(screen.getByText(/reset/i));
// Assert that the form fields are cleared
expect(screen.getByLabelText(/name/i).value).toBe('');
expect(screen.getByLabelText(/email/i).value).toBe('');
});
4. Best Practices for Writing Test Cases for Forms
- Test Validation: Always test both required field validation and format validation (e.g., email format, password strength).
- Test User Input: Simulate real user input with
fireEvent.changeto ensure that the form fields update as expected. - Test Form Submission: Test that the form’s onSubmit function is called with the correct data when the form is valid.
- Test Error Handling: Ensure that error messages are displayed when validation fails and that they are cleared once valid input is provided.
- Test Edge Cases: Consider edge cases like empty fields, very large input values, and special characters.
- Test User Interactions: Test how the form behaves when a user interacts with it, including actions like focusing on fields, clicking buttons, and submitting the form.
