I am trying to test the onChange
event of a Select component using react-testing-library.
I grab the element using getByTestId
which works great, then set the value of the element and then call fireEvent.change(select);
but the onChange
is never called and the state is never updated.
I have tried using both the select component itself and also by grabbing a reference to the underlying input
element but neither works.
Any solutions? Or is this a know issue?
This turns out to be super complicated when you are using Material-UI's Select
with native={false}
(which is the default). This is because the rendered input doesn't even have a <select>
HTML element, but is instead a mix of divs, a hidden input, and some svgs. Then, when you click on the select, a presentation layer (kind of like a modal) is displayed with all of your options (which are not <option>
HTML elements, by the way), and I believe it's the clicking of one of these options that triggers whatever you passed as the onChange
callback to your original Material-UI <Select>
All that to say, if you are willing to use <Select native={true}>
, then you'll have actual <select>
and <option>
HTML elements to work with, and you can fire a change event on the <select>
as you would have expected.
Here is test code from a Code Sandbox which works:
import React from "react";
import { render, cleanup, fireEvent } from "react-testing-library";
import Select from "@material-ui/core/Select";
beforeEach(() => {
jest.resetAllMocks();
});
afterEach(() => {
cleanup();
});
it("calls onChange if change event fired", () => {
const mockCallback = jest.fn();
const { getByTestId } = render(
<div>
<Select
native={true}
onChange={mockCallback}
data-testid="my-wrapper"
defaultValue="1"
>
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</Select>
</div>
);
const wrapperNode = getByTestId("my-wrapper")
console.log(wrapperNode)
// Dig deep to find the actual <select>
const selectNode = wrapperNode.childNodes[0].childNodes[0];
fireEvent.change(selectNode, { target: { value: "3" } });
expect(mockCallback.mock.calls).toHaveLength(1);
});
You'll notice that you have to dig down through the nodes to find where the actual <select>
is once Material-UI renders out its <Select>
. But once you find it, you can do a fireEvent.change
on it.
The CodeSandbox can be found here: