I am trying to use jest
and @testing-library/react
to verify that a react component (which wraps an HTML input
element) is calling it’s onChange
callback with the correct value in response to a ChangeEvent.
Here is the react component
export const MyInput = ({ onChange, value }) => {
const handleChange = (event) => {
console.log(event.target.value);
onChange(event);
};
return (
<div>
<input
type="text"
onChange={handleChange}
label={"test-component"}
data-testid={"test"}
value={value}
/>
</div>
);
};
And here is the test
test("onChange should work", () => {
const onChangeMock = jest.fn();
let inputValue = 1;
const { getByTestId } = render(
<MyInput onChange={onChangeMock} value={inputValue} />
);
const input = getByTestId("test");
fireEvent.change(input, { target: { value: "23" } });
expect(onChangeMock).toHaveBeenCalled();
expect(onChangeMock).toHaveBeenCalledWith(
expect.objectContaining({
target: expect.objectContaining({
value: "23"
})
})
);
});
And here is a link to a codesandbox which reproduces the problem.
The second assertion in my tests fails, because the onChangeMock
‘s caller is an event whose’ target.value
is equal to 1
, rather than 23
.
And what’s even weirder is that when logging event.target.value
inside the handleChange
function – it has the correct value of 23
. Inspecting with a debugger also shows that the jest mock has a correct caller (event with a target value of 23
up until the debugger leaves the scope of the handleChange
function). The moment I get back to the test scope and perform the assertion, the mock’s event has a target value of 1.
So I am trying to understand Why am I observing this behavior and what is causing it ?
(I assume that the event’s target gets set (by what though?) to my html input element the moment the event handler scope ends, and jest does not capture a deep clone of the event, as it was passed to the external function, but I could not find any documentation to support this)
2
Answers
Your text input is not effecting changes because your code is not treating it like a Controlled Input.
In short, the test is expecting the right things, but the component is ignoring changes.
Try this:
You’ll see a similar example in Testing Library InputEvent docs
React uses synthetic events instead of native DOM events, they behave similarly but not exactly the same, synthetic events are nullified after the handler handler is executed, for illustration, try this with the event handler from your code: