testing library: user events

2020-09-12

 | 

~3 min read

 | 

415 words

When it comes to testing a website, often times you’ll be interested in seeing how the site behaves after a user interacts with it. That means that you’ll want to fire events. With testing-library, there’s the well named fireEvent API to do exactly that. There are some limitations to using fireEvent which the team outlines in their guide to events. Specifically, the fireEvent does exactly that, it only fires an event. When a user interacts with a UI, however, they often fire multiple events.

Consider fireEvent.click which creates a click event and dispatches that event on the given DOM node. This works properly for most situations when you simply want to test what happens when your element is clicked, but when the user actually clicks your element, these are the events that are typically fired (in order):

  • fireEvent.mouseOver(element)
  • fireEvent.mouseMove(element)
  • fireEvent.mouseDown(element)
  • element.focus() (if that element is focusable)
  • fireEvent.mouseUp(element)
  • fireEvent.click(element)

Most of the time this will not make a difference for tests. fireEvent.click() should be sufficient. However, I recently came across a situation where I think it did, which caused me considerable frustration until I actually read the notice at the top of the fireEvent documentation:

(Side note: Somewhere along the way a <blockquote> element stopped being something folks read. I sent this page to multiple peers and we all skipped the “announcement”. It’s a shame too, because, at least in this case, it held the answer to my pain.)

Replacing fireEvent with userEvent in my tests did require adding a dependency (it’s a part of the @testing-library/user-event package), but it also addressed a bug in my code. Specifically, I was texting to see what would happen after a checkbox was clicked. When I used fireEvent, the checkbox remained unchecked. Converting to userEvent fixed this:

testingChecks.js
await waitFor(() => {
  const checkbox = getByTestId('checkbox');
  expect(checkbox).toBeDisabled();
  if (checkbox) {
-    fireEvent.click(checkbox);
+    userEvent.click(checkbox);
  }
  expect(checkbox.checked).toBe(true);
});

This works because the userEvent actually does simulate the interaction, not just the click. From the README:

user-event tries to simulate the real events that would happen in the browser as the user interacts with it. For example userEvent.click(checkbox) would change the state of the checkbox.

It does make me a bit better that the headliner for why to use the user-event is what I was struggling with! I suppose this is also a good reminder to pay closer attention to the “announcements” going forward!


Hi there and thanks for reading! My name's Stephen. I live in Chicago with my wife, Kate, and dog, Finn. Want more? See about and get in touch!