Reputation: 191
I have an Input component, which accepts a prop method and calls it when the user types something in. Code itself works as expected, but for some reasons, test fails. It thinks that prop method wasn't called. Why is it happening? For testing purposes, I use Jest and react-testing-library.
And second question. In real application, my idea is to test parameters that were passed to that prop method. Is it considered to be an implementation testing (I know that I should test it)?
Input.js
export default function Input({ onChange }) {
return <input onChange={onChange} />;
}
Test
import React from "react";
import { render, act, cleanup, fireEvent } from "react-testing-library";
import Input from "./input";
describe("Input tests", () => {
afterEach(cleanup);
it("Should call prop function", () => {
const onChange = jest.fn();
const { getByTestId } = render(<Input onChange={onChange} />);
const input = getByTestId("input");
act(() => {
fireEvent.change(input, { target: { value: "Q" } });
});
expect(onChange).toHaveBeenCalled();
});
});
https://codesandbox.io/s/y229669nvx
Upvotes: 4
Views: 10459
Reputation: 26978
The reason why your test doesn't work is that you're using getByTestId
to find your element. getByTestId
looks for a DOM node that has a data-testid
attribute.
In order to make your test pass, you have various options.
You could add a data-testid
to your input
: <input data-testid="input" onChange={onChange} />
. This would work, however, it's better to avoid test ids whenever you can.
In a real application, your input would be rendered with a label
, we can take advantage of that:
const { getByLabelText } = render(
<label>
My input
<Input onChange={onChange} />
</label>
)
const input = getByLabelText('My input')
Another solution is to use container
which is one one of the values returned by render
. It's a DOM node—like everything else in RTL—so you can use the usual DOM APIs:
const { container } = render(<Input onChange={onChange} />)
// Any of these would work
const input = container.firstChild
const input = container.querySelector('input')
As a side note, I agree that RTL tests seem more complicated if compared to Enzyme. There's a good reason for it. RTL pushes you to test your application as if it were a black box. This is a bit harder to do in the beginning but ultimately leads to better tests.
Enzyme, on the other hand, mocks most things by default and allows you to interact with your components implementation. This, in my experience, looks easier in the beginning but will produce brittle tests.
I encourage you to join the spectrum channel if you need help getting started.
Upvotes: 0
Reputation: 19762
After reading this, it looks like it's by design to not assert against events handlers. Although it appears to work in React 16.5, however, using 16.8.x fails. I'd suggest moving to enzyme if you want to test such features.
Testing with react-testing-library
fails (however, as you'll notice, when running the test, the input's value will actually change): https://codesandbox.io/s/n3rvy891n4
Testing with enzyme
succeeds: https://codesandbox.io/s/lx34ny41nl
Upvotes: 1