How to Mock Synthetic Events in React
Testing user interactions in React often requires simulating events that conform to React’s custom event system. This article provides a straightforward guide on how to mock Synthetic Events in React using Jest and React Testing Library, allowing you to easily test event handlers, custom event properties, and preventDefault behaviors in your unit tests.
Why Mock Synthetic Events?
React uses a wrapper called SyntheticEvent to ensure
native browser events behave consistently across different browsers.
When writing unit tests, you may need to mock these events to: * Verify
that event methods like preventDefault() or
stopPropagation() are called. * Test standalone event
handler functions without rendering the entire component tree. * Pass
custom event payloads (like input values or key codes) to your
handlers.
Method 1: Mocking Synthetic Events with React Testing Library
The most common and recommended way to test events in React is using
React Testing Library (RTL). RTL’s
fireEvent utility automatically creates and dispatches
simulated Synthetic Events for you.
Here is how you can use fireEvent to mock an input
change event with a specific value:
import { render, screen, fireEvent } from '@testing-library/react';
import MyComponent from './MyComponent';
test('updates input value on change', () => {
render(<MyComponent />);
const inputElement = screen.getByRole('textbox');
// fireEvent mocks the SyntheticEvent and passes the target value
fireEvent.change(inputElement, { target: { value: 'Hello World' } });
expect(inputElement.value).toBe('Hello World');
});Method 2: Manually Mocking the Event Object
If you are unit testing a specific event handler function directly (without rendering the component) or need to assert that specific event methods were triggered, you can construct a manual mock event object using Jest.
Here is an example of mocking a form submission event where
preventDefault must be called:
The Component and Handler
export const handleSubmit = (event, callback) => {
event.preventDefault();
callback(event.target.username.value);
};The Jest Test
import { handleSubmit } from './formHandlers';
test('calls preventDefault and passes form data on submit', () => {
const mockCallback = jest.fn();
// Manually mock the SyntheticEvent structure
const mockEvent = {
preventDefault: jest.fn(),
target: {
username: { value: 'john_doe' }
}
};
handleSubmit(mockEvent, mockCallback);
// Assertions
expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1);
expect(mockCallback).toHaveBeenCalledWith('john_doe');
});Method 3: Mocking Persist for Older React Versions
In React 16 and earlier, Synthetic Events were pooled, meaning the
event object was reused and its properties nullified after the callback
ran. To keep the properties around, you had to call
event.persist().
If you are testing legacy code that relies on
event.persist(), your manual mock object must include a
mock implementation of this method to prevent runtime errors:
const mockEvent = {
persist: jest.fn(), // Prevents "TypeError: event.persist is not a function"
target: { value: 'some value' }
};(Note: Event pooling was removed in React 17, so
persist() is no longer necessary for modern React
applications, though the method still exists as a no-op.)