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.)