React testing library: how to explicitly reuse rendered component without removing auto-cleanup

Viewed 1661
  1. RTLib has auto-cleanup and I want to keep it.
  2. In some cases though, I want to reuse the render result of my component in that way (tests are simplified):
describe('some set of related functionality', () => {
    const onSelect = jest.fn();
    const Wrapper = render(
      <MyComponent onSelect={onSelect)} />
    );

    afterEach(() => {
      onSelect.mockReset();
    });

    it('tests something', async () => {
      userEvent.click(await Wrapper.findByText('some-text'));
      expect(onSelect).toBeCalledWith('something');
    });

    it('also tests something on the same component very related to closest describe block', async () => {
      userEvent.click(await Wrapper.findByText('some-other-text'));
      expect(onSelect).toBeCalledWith('some-other-thing');
    });
});

so the idea here is to reuse the Wrapper between some tests and query over that wrapper instead of the global screen which is cleaned in global afterEach.

I like the default behaviour but I think it might be useful to reuse the wrapper between some tests, e.g. to speed up some tests or make them shorter.

The alternative, for now, is to write many assertions (many tests in fact) in a single it statement. E.g. it can be like this instead

it('tests some set of related functionality', () => {
    const onSelect = jest.fn();
    render(<MyComponent onSelect={onSelect)} />);

    // tests something
    userEvent.click(await Wrapper.findByText('some-text'));
    expect(onSelect).toBeCalledWith('something');

    // have to do it manually now
    onSelect.mockReset();

    // also tests something on the same component
    userEvent.click(await Wrapper.findByText('some-other-text'));
    expect(onSelect).toBeCalledWith('some-other-thing');
});

The motivation here is:

  1. Tests speed
  2. An ability to write a sequence of tests where every step is a test case by itself, without the need to repeat render code and previous steps (i.e. user interactions) to reach a certain component state.

Is there any way to achieve that?

1 Answers

This question is a bit subjective, but as the creator of the library you're using you may be interested in my opinion (especially since you emailed me a link to this)

The alternative you mention is the recommended approach. Read more in Write fewer, longer tests.

Also, I noticed you're calling the return value of render Wrapper which is old cruft from enzyme and I recommend avoiding that as well. Read more about that in Common mistakes with React Testing Library.

I hope that's helpful.

Related