How to Use fireEvent in React Testing with Jest

I remember the early days of writing front-end tests. I’d stare at the DOM, wondering how to simulate what users actually do—clicking buttons, typing in fields, changing options. That’s when I discovered fireEvent from React Testing Library, and everything started making sense.

While testing user interfaces, it’s easy to fall into the trap of directly calling functions or triggering effects manually. But that doesn’t reflect what real users do. If your goal is to build user-centric and robust tests, fireEvent is your go-to weapon.

In this blog, I’ll guide you through how fireEvent works with React Testing Library and Jest, practical use cases, common mistakes, and how to combine it with assertion best practices to create meaningful, user-driven tests.


The Problem: Manual Function Calls ≠ Real User Interactions

Here’s what I did wrong in my early tests:

import { render } from '@testing-library/react';
import Counter from './Counter';

test('increments the counter', () => {
  const { container } = render(<Counter />);
  // Directly calling the function instead of simulating the user action
  container.querySelector('button').click();
  expect(container.textContent).toContain('1');
});

This technically works, but it bypasses React’s event system, and doesn’t test actual browser behavior. That’s where fireEvent steps in.


What is fireEvent?

fireEvent is a utility from React Testing Library that allows you to simulate DOM events (like click, change, submit, keydown, etc.) in tests—just like a real user interaction would trigger them.

Unlike calling onClick manually, fireEvent goes through the proper bubbling, propagation, and synthetic event handling that React expects.


Getting Started with fireEvent

First, make sure you have:

npm install --save-dev @testing-library/react @testing-library/jest-dom

Let’s start with a simple example—a counter.

// Counter.js
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <>
      <p data-testid="count">{count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}

export default Counter;

Now, test it using fireEvent:

import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('increments counter on click', () => {
  render(<Counter />);
  const button = screen.getByText('Increment');
  fireEvent.click(button);
  expect(screen.getByTestId('count')).toHaveTextContent('1');
});

And just like that, you’re simulating real user clicks in Jest!


Common fireEvent Use Cases

1. Text Input

<input type="text" onChange={(e) => setName(e.target.value)} />
fireEvent.change(screen.getByRole('textbox'), { target: { value: 'John' } });

āœ… Simulates typing into an input.


2. Form Submission

fireEvent.submit(screen.getByRole('form'));

This triggers a form’s onSubmit handler as if the user pressed Enter or clicked a submit button.


3. Checkboxes and Radios

fireEvent.click(screen.getByLabelText('Accept Terms'));

Or directly:

fireEvent.change(screen.getByLabelText('Accept Terms'), { target: { checked: true } });

4. Keyboard Events

fireEvent.keyDown(input, { key: 'Enter', code: 'Enter' });

Great for testing accessibility and keyboard-driven navigation.


fireEvent vs userEvent

You might ask: should I even use fireEvent anymore?

Well, fireEvent is low-level, whereas userEvent is built to mimic more realistic user interactions.

Here’s the difference:

// fireEvent
fireEvent.change(input, { target: { value: 'React' } });

// userEvent (recommended)
await userEvent.type(input, 'React');

Use fireEvent when:

  • You’re testing simple components.
  • You want full control over event payloads.
  • You need custom event triggers (like programmatic focus or blur).

But for simulating full flows, userEvent feels more natural.


Best Practices I’ve Learned

āœ… Always Test Behavior, Not Implementation

Don’t test internal state or call setState directly. Use fireEvent to drive your UI and assert outcomes that the user sees.

āœ… Use screen for Better Readability

Avoid destructuring getByText etc. from the render result. screen is recommended:

fireEvent.click(screen.getByText('Submit'));

āœ… Assert with @testing-library/jest-dom

Make assertions more expressive:

expect(button).toBeDisabled();
expect(input).toHaveValue('hello');
expect(div).toBeVisible();

Debugging fireEvent Failures

When tests fail, try:

screen.debug();

It prints the current DOM tree, helping you figure out what’s happening.

Also, always double-check your element selectors and make sure await is used properly if you’re testing async behavior.

If you want a solid async test guide, check out my post:
šŸ‘‰ Top 10 React Testing Library Queries Every Developer Should Know


fireEvent with Controlled Components

Let’s test a controlled input:

function NameInput() {
  const [name, setName] = useState('');
  return (
    <input
      aria-label="name-input"
      value={name}
      onChange={(e) => setName(e.target.value)}
    />
  );
}

Test:

fireEvent.change(screen.getByLabelText('name-input'), {
  target: { value: 'Waleed' },
});
expect(screen.getByLabelText('name-input')).toHaveValue('Waleed');

Final Thoughts

Testing isn’t just about making sure code ā€œrunsā€ā€”it’s about ensuring the user gets what they expect.

fireEvent helped me shift my mindset from unit testing to behavior-driven testing. By simulating real user actions, I write better code and gain confidence in every UI change.

Start small. Test a button click. A form submission. Then build up. I promise, writing clean, readable, and reliable tests will become second nature.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top