← Back to React Course | Chapter 12: Testing React Applications | Lesson 4 of 9

Simulating User Events

Simulating user events in tests is like a robot actor pressing buttons and typing on a keyboard exactly the way a real visitor would, so you can watch how your app reacts.

Simulating a Click

userEvent.click(element) simulates a real user clicking an element, including the full sequence of events a browser would actually fire (pointerdown, mousedown, focus, etc.), more accurately than the more basic fireEvent.click, which only fires a single click event.

Note: Always await userEvent methods — they return Promises to accurately model the timing of real interactions.

Warning: In newer versions of the library, you must call userEvent.setup() first and use the returned object, rather than calling userEvent.click directly.

Example: Simulating a Click

markup
// Run in your local React project (npm install required)
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Counter from './Counter';

test('increments on click', async () => {
  const user = userEvent.setup();
  render(<Counter />);
  await user.click(screen.getByText('Increment'));
  expect(screen.getByText('Count: 1')).toBeInTheDocument();
});

⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.

Simulating Typing into an Input

userEvent.type(element, text) simulates a user typing character by character, triggering the same sequence of keyboard and input events a real keystroke would, which is more realistic than setting an input's value directly.

Note: This realism matters for components with logic tied to individual keystrokes, like a live character counter or format-as-you-type input.

Warning: Typing simulated this way is genuinely character-by-character, so it can make tests slightly slower than directly setting a value — usually a worthwhile tradeoff for accuracy.

Example: Simulating Typing into an Input

markup
// Run in your local React project (npm install required)
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import SearchBox from './SearchBox';

test('updates as user types', async () => {
  const user = userEvent.setup();
  render(<SearchBox />);
  await user.type(screen.getByRole('textbox'), 'react');
  expect(screen.getByDisplayValue('react')).toBeInTheDocument();
});

⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.

Testing a Full Form Submission Flow

Combining several userEvent calls in sequence lets you simulate a complete realistic flow — typing into fields, then clicking submit — mirroring exactly what a real person would do, and verifying the resulting behavior (like a success message appearing).

Note: Chain userEvent calls with await between each step, mirroring the real order a user would perform them in.

Warning: Forgetting to await each userEvent call in a multi-step flow can cause steps to race each other, producing flaky, inconsistent test results.

Example: Testing a Full Form Submission Flow

markup
// Run in your local React project (npm install required)
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import LoginForm from './LoginForm';

test('submits the login form', async () => {
  const user = userEvent.setup();
  render(<LoginForm />);
  await user.type(screen.getByLabelText('Email'), '[email protected]');
  await user.click(screen.getByRole('button', { name: 'Log In' }));
  expect(await screen.findByText('Welcome back!')).toBeInTheDocument();
});

⚠️ This example uses an npm package with no CDN build available here — run this in your local React project.

Common Mistakes
  1. Using fireEvent for typing when userEvent more accurately simulates real keystroke-by-keystroke typing behavior.
  2. Forgetting that most userEvent methods are async and need await.
  3. Not setting up userEvent with userEvent.setup() in newer versions of the library before using it.
Chapter Summary
  • @testing-library/user-event simulates real user interactions more realistically than the lower-level fireEvent API.
  • userEvent.click, userEvent.type, and similar methods are async and should be awaited.
  • userEvent.setup() creates a user-event instance, typically called once at the top of a test.
  • Simulating realistic interactions catches bugs that a more artificial fireEvent might miss.
Browser Support

Requires npm install @testing-library/user-event — runs in a Node test environment, not this browser sandbox.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.