Skip to content

React Testing Library unittest

Jean-Michel FRANCOIS edited this page May 3, 2023 · 3 revisions

Context

We Have to migrate from enzyme to RTL because React 18 has a internal breaking change that makes enzyme dead by design. React now schedule render. So we have to rewrite a bunch of tests suite (360).

But RTL came with a real difference: no access to react component, no access to state. It looks like a more end to end approach. As we still need unittest we have to find tricks to tests only one component and not the entire subset.

Also RTL is based on jsdom so there is limitations, we will try to list them.

Snapshot

Snapshot are nice to keep a trace of the render. Just do it once per component seems enough. This is usefull to see impact when a DOM is changed for example after upgrading a library.

Avoid doing 10 snapshots has they do not fit any tests except it('should render', () => {

mock dependencies

Component is about limitation of the responsabilities, so it is ok to mock underlying components or dependencies. This is not considered as a code smell ref as mentionned in this article, in the context of composition.

The goal of mock can be:

  • make it easier to test the current component
  • get the internal state of the component you test

First tips is to use dataset.props:

jest.mock('../../pickers/CalendarPicker', () => {
	return props => <div data-testid="CalendarPicker" data-props={JSON.stringify(props)} />;
});
// ..tests
		// then
		const props = JSON.parse(screen.getByTestId('CalendarPicker').dataset.props);
		expect(props).toMatchObject({
			manageFocus: true,
			other: 'custom props',
			selectedDate: '2007-01-01T23:00:00.000Z',
			useUTC: false,
		});

The drawback is you don't have access to function but this is often OK as function are most of the time event handler. You can add buttons to manage event handler out of the scope of your current component. But this is usefull only if the current component create this handler. if it is only a pass through it doesn't make sens to unittest it.

jest.mock('../../views/DateTimeView', () =>
	jest.fn(props => (
		<div data-testid="DateTimeView" data-props={JSON.stringify(props)}>
			<button onClick={() => props.onTitleClick()}>Select MonthYearView</button>
		</div>
	)),
);

We should avoid mock far away dependency, for example if A depends on B depends on C and you tests A avoid to mock C and prefer to mock B.

Limitations

As we use jsdom it means it is not a real browser, so no graphical representation, only part of the DOM.

  • scroll or wheel event are not supported
  • getBoundingClientRect does not exists

If you fall into this case you have the following choices

  • try to add the missing function on DOM
  • use a real browser (slower tests)
window.HTMLElement.prototype.getBoundingClientRect = () => ({ width: 42 });

Migration guide

wrapper.setProps => rerender

- const wrapper = mount(
+ const { rerender} = render(
//...
- wrapper.setProps({
+ rerender( // same react but now with this new props