This article is about fixing error when Using fireEvent.change() on a select element fires the event handler, but doesn't update state.
  • 01-Feb-2023
Lightrun Team
Author Lightrun Team
Share
This article is about fixing error when Using fireEvent.change() on a select element fires the event handler, but doesn't update state.

Using fireEvent.change() on a select element fires the event handler, but doesn’t update state in Testing Library React Testing Library

Lightrun Team
Lightrun Team
01-Feb-2023

Explanation of the problem

The test environment includes the @testing-library/react library version 11.2.5, the Jest testing framework version 4.0.2 through react-scripts, a jsdom DOM environment version 16.4.0 (also through react-scripts), and Node v12.18.0.

The issue being encountered is with attempting to change a <select> element in a React app and to check that different content is displayed based on what is selected. The code used to change the <select> element and check the content is as follows:

const selectOne = screen.getByRole("combobox", { name: "My select" });
fireEvent.change(selectOne, {
  target: { value: "OPTION1" }
});

expect(screen.getByText("OPTION1")).toBeInTheDocument();

Despite the fact that the event handler is firing, the state of the app is not changing as expected. Console logs in the event handler show that the state is not changing and the tests are failing to recognize the change.

Reproduction and Details

A stripped down version of the project has been created in CodeSandbox to attempt to identify the problem, with the same failing test behavior being encountered. The CodeSandbox link can be found here: https://codesandbox.io/s/react-testing-library-demo-forked-v09xi?file=/src/App.js. The issue is that the tests are not changing the app state in the same way as the browser. The root cause is not yet clear.

Troubleshooting with the Lightrun Developer Observability Platform

Getting a sense of what’s actually happening inside a live application is a frustrating experience, one that relies mostly on querying and observing whatever logs were written during development.
Lightrun is a Developer Observability Platform, allowing developers to add telemetry to live applications in real-time, on-demand, and right from the IDE.

  • Instantly add logs to, set metrics in, and take snapshots of live applications
  • Insights delivered straight to your IDE or CLI
  • Works where you do: dev, QA, staging, CI/CD, and production

Start for free today

Problem solution for Using fireEvent.change() on a select element fires the event handler, but doesn’t update state in Testing Library React Testing Library

The problem is because fireEvent.change() is not designed to work with select elements. To update the state of a select element, use fireEvent.select() instead. Here’s an example:

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

const { container } = render(<select value="B" onChange={handleChange}>
  <option value="A">A</option>
  <option value="B">B</option>
  <option value="C">C</option>
</select>);

const select = container.firstChild;

fireEvent.select(select, { target: { value: 'C' } });

expect(handleChange).toHaveBeenCalledWith(expect.objectContaining({
  target: { value: 'C' },
}));

Other popular problems with Testing Library React Testing Library

Problem: Testing of asynchronous behavior with Testing Library React Testing Library

One common issue developers face while testing with Testing Library React Testing Library is testing asynchronous behavior such as API calls. Since these tests are executed in an asynchronous manner, it can be challenging to make assertions about the component’s state after an API call. To overcome this, you can use the waitFor utility from Testing Library React Testing Library that allows you to wait for a condition to be met before moving on to the next test step.

Solution:

You can use the waitFor utility to wait for an element to appear or for a certain condition to be met. For example, if you want to wait for a loading indicator to disappear before making assertions about the component’s state, you can use the following code:

const { getByText } = render(<MyComponent />);

await waitFor(() => {
  expect(getByText('Loading...')).not.toBeInTheDocument();
});

expect(getByText('Data Loaded')).toBeInTheDocument();

Problem: Testing of component interactions with Testing Library React Testing Library

Another issue that developers face while testing with Testing Library React Testing Library is testing component interactions. For example, if a component updates its state based on user interactions, it can be difficult to test these interactions with a static render of the component.

Solution:

Another issue that developers face while testing with Testing Library React Testing Library is testing component interactions. For example, if a component updates its state based on user interactions, it can be difficult to test these interactions with a static render of the component.

const { getByText } = render(<MyComponent />);

fireEvent.click(getByText('Click Me'));

expect(getByText('State Updated')).toBeInTheDocument();

Problem: Testing of component updates with Testing Library React Testing Library

A third issue developers face while testing with Testing Library React Testing Library is testing component updates. For example, if a component updates its state based on some external data, it can be difficult to test these updates.

Solution:

You can use the render utility from Testing Library React Testing Library with a custom render function that allows you to provide updated props to the component. For example, if you have a component that updates its state based on a count prop, you can use the following code to test it:

const { getByText } = render(<MyComponent count={0} />);

expect(getByText('Count: 0')).toBeInTheDocument();

render(<MyComponent count={1} />);

expect(getByText('Count: 1')).toBeInTheDocument();

A brief introduction to Testing Library React Testing Library

Testing Library React Testing Library is a testing utility for React applications that helps developers write maintainable and reliable tests for their components. It provides a simple and intuitive API for testing React components that focuses on the behavior of the component being tested, rather than the implementation details. This makes it easier for developers to write tests that are less likely to break when the implementation of the component changes.

Testing Library React Testing Library works by rendering the component being tested into a virtual DOM and then simulating user interactions with the component using utility functions such as fireEvent. The tests then make assertions about the state of the component and the resulting HTML structure. This approach allows developers to test the behavior of the component as it would appear to the user, without having to worry about the underlying implementation details. Additionally, Testing Library React Testing Library provides utility functions for waiting for elements to appear, for waiting for a certain condition to be met, and for finding elements within the virtual DOM, making it easier for developers to write tests that are less brittle and more reliable.

Most popular use cases for Testing Library React Testing Library

  1. Testing the behavior of React components: Testing Library React Testing Library can be used to test the behavior of React components by rendering the components into a virtual DOM and then simulating user interactions with the components. This allows developers to test the component’s behavior as it would appear to the user, without having to worry about the underlying implementation details.
const { getByText } = render(<MyComponent />);

fireEvent.click(getByText('Click Me'));

expect(getByText('State Updated')).toBeInTheDocument();
  1. Testing the state of React components: Testing Library React Testing Library can be used to test the state of React components by making assertions about the state of the component after user interactions. This allows developers to ensure that the component’s state is updated correctly in response to user interactions.
const { getByText } = render(<MyComponent />);

expect(getByText('Initial State')).toBeInTheDocument();

fireEvent.click(getByText('Click Me'));

expect(getByText('Updated State')).toBeInTheDocument();
  1. Testing the structure of the HTML generated by React components: Testing Library React Testing Library can be used to test the structure of the HTML generated by React components by making assertions about the structure of the virtual DOM. This allows developers to ensure that the component’s HTML structure is correct, which is important for accessibility and other aspects of the user experience.
const { container } = render(<MyComponent />);

expect(container.firstChild).toHaveClass('container');

expect(container.firstChild.firstChild).toHaveClass('header');
Share

It’s Really not that Complicated.

You can actually understand what’s going on inside your live applications.

Try Lightrun’s Playground

Lets Talk!

Looking for more information about Lightrun and debugging?
We’d love to hear from you!
Drop us a line and we’ll get back to you shortly.

By submitting this form, I agree to Lightrun’s Privacy Policy and Terms of Use.