Reputation: 2044
I'm presently learning React-Testing-Library.
I'd like to test mouse interaction with an element. Presently it's a bit unclear to me the difference between userEvent.click(element)
and fireEvent.click(element)
.
Are both recommended for use, and in the example below are they being correctly implemented?
const mockFunction = jest.fn(() => console.info('button clicked'));
const { getByTestId } = render(<MyAwesomeButton onClick={mockFunction} />);
const myAwesomeButton = getByTestId('my-awesome-button');
// Solution A
fireEvent(myAwesomeButton)
expect(mockFunction.toHaveBeenCalledTimes(1);
// Solution B
userEvent.click(myAwesomeButton);
expect(mockFunction).toHaveBeenCalledTimes(1);
Thanks in advance for any clarity.
Upvotes: 84
Views: 98593
Reputation: 2243
I want to build upon the accepted answer from @Guilhermevrs. There has been one change since, and that is that userEvent
doesn't use fireEvent
from v14 on, but this doesn't affect userEvent.click()
directly.
To mimic user interactions like clicking buttons or inputting text for testing, the Testing Library provides various APIs. These APIs are:
fireEvent
: This synchronous, low-level API works as a wrapper around the dispatchEvent
Web API. For example, you can trigger a click
event on a button using fireEvent.click(buttonElement)
which would trigger an onClick
handler function on the component.userEvent
: An asynchronous, high-level API, userEvent
simulates real user behaviour by triggering multiple events within a single interaction. The userEvent
offers two primary methods to simulate keyboard and pointer interactions, along with utility methods expanding on these.
userEvent.keyboard()
: This method replicates events like keyDown
and keyUp
using the dispatchEvent
Web API.userEvent.pointer()
: Similar to userEvent.keyboard()
, this method triggers a variety of pointer events using the dispatchEvent
Web API. For example, userEvent.pointer({ keys: '[MouseLeft]', target: buttonElement})
triggers the pointerDown
, mouseDown
, pointerUp
, mouseUp
and click
event.userEvent.type()
: This method emulates a click
, keydown
, keyup
and input
event. Hence, userEvent.type(inputElement, 'hello')
triggers onClick
, onKeyDown
, onKeyPress
, onKeyUp
, and onChange
handlers on the component.userEvent.click()
: This method emulates a hover
and click
event. For instance, userEvent.click(buttonElement)
activates all the events from userEvent.pointer
with mouseEnter
, pointerEnter
, mouseOver
and pointerOver
, but no mouseLeave
and pointerLeave
since the pointer will stay on the element.Before version 14,
userEvent
internally usedfireEvent
. There was, however, a need foruserEvent
to have more control over the changes happening to the DOM. As a result, the internal calls tofireEvent
have been removed in the later versions anduserEvent.keyboard
anduserEvent.pointer
themselves now expand on thedispatchEvent
Web API
While userEvent
is usually the preferred option because of its high-level abstraction and realism, you can fallback on fireEvent
if you need to mimic edge case interactions that userEvent
doesn't support.
userEvent
limitations
To name a few limitations ofuserEvent
:
- it does not yet fully support keyboard composition sessions (#1097), which is the ability to simulate a user typing in text through the keyboard in a specific manner.
- it inaccurately handles events in number input fields, where pressing ArrowUp/ArrowDown does not trigger an onChange event (#1066).
- it falls short in simulating the interaction with
datalist
elements (#1088).These limitations are not typically encountered in daily use cases. To circumvent these issues, consider using the
keyboard
orpointer
modules. Alternatively, the lower-level APIfireEvent
can be used.
It should also be noted that using userEvent
trades performance for accuracy. You remove risks of incorrect assumptions in your test - such as an why onKeyDown
event not being triggered - when a seemingly simple thing like typing into a textbox is performed. But hence, userEvent
can be 10x slower than a single fireEvent
.
Upvotes: 7
Reputation: 3796
another good to mention difference between fireEvent
and userEvent
is that
by default fireEvent is wrapped inside act
function and this is useful when the user does some action and this action will cause component updates and re-render. On the contrary, if we used userEvent
probably will notice "not wrapped in act(...)" warning
error that appears in the console.
act(() => {
userEvent.type(input, 'inputValue')
})
and here we don't need the act function, cause it's already wrapped over fireEvent
fireEvent.change(input, {target: {value: 'inputValue'}})
and this great article demonstrates this concept Common mistakes with React Testing Library
Upvotes: 5
Reputation: 1019
According to Docs, you should use user-event
to test interaction with your components.
fireEvent
dispatches exactly the events you tell it to and just those - even if those exact events never had been dispatched in a real interaction in a browser.
User-event
on the other hand dispatches the events like they would happen if a user interacted with the document. That might lead to the same events you previously dispatched per fireEvent
directly, but it also might catch bugs that make it impossible for a user to trigger said events.
Upvotes: 34
Reputation: 2369
Behind the scenes, userEvent
uses the fireEvent
. You can consider fireEvent
being the low-level api, while userEvent
sets a flow of actions.
Here is the code for userEvent.click
You can see that depending of which element you are trying to click, userEvent
will do a set of different actions (e.g. if it's a label or a checkbox).
Upvotes: 105