Search code examples
javascripttypescriptjestjsreact-testing-library

How to remove cleanup in Jest


I'd like to write unit tests and make them really small. But I have a problem. React testing library unmounts root component every time test passes. So, I can do this

describe('Some test', () => {
  const { rerender } = render(<Component prop1={0} />);

  test('Title 1', () => {
    // logic1
  });

  test('Title 2', () => {
    rerender(<Component prop1={0} />);
    // logic2
  });

  test('Title 3', () => {
    rerender(<Component prop1={1} />);
    // logic3
  });

  test('Title 4', () => {
    rerender(null);
    // logic4
  });
});

I'd like to call rerender function in each test and then add a specific logic for each test. But I'm getting an error Error: Cannot update an unmounted root. from the 2nd test onwards.

I can write my test in this way

test('Some test', () => {
  const { rerender } = render(<Component prop1={0} />);

  // logic1

  rerender(<Component prop1={0} />);
  // logic2

  rerender(<Component prop1={1} />);
  // logic3

  rerender(null);
  // logic4
});

But in this case the test becomes too massive.

So, the question is: Is it possible to write tests in the first example? If yes, how is it possible?


Solution

  • Skipping Auto Cleanup

    you could import @testing-library/react/pure in all your tests that you don't want the cleanup to run and the afterEach won't be setup automatically.

    If we import from @testing-library/react, it will import from index.js file.

    src/index.js:

    import {cleanup} from './pure'
    
    // if we're running in a test runner that supports afterEach
    // or teardown then we'll automatically run cleanup afterEach test
    // this ensures that tests run in isolation from each other
    // if you don't like this then either import the `pure` module
    // or set the RTL_SKIP_AUTO_CLEANUP env variable to 'true'.
    if (typeof process === 'undefined' || !process.env?.RTL_SKIP_AUTO_CLEANUP) {
      // ignore teardown() in code coverage because Jest does not support it
      /* istanbul ignore else */
      if (typeof afterEach === 'function') {
        afterEach(() => {
          cleanup()
        })
      } else if (typeof teardown === 'function') {
        // Block is guarded by `typeof` check.
        // eslint does not support `typeof` guards.
        // eslint-disable-next-line no-undef
        teardown(() => {
          cleanup()
        })
      }
    }
    
    export * from './pure';
    

    As you can see in the source code, RTL calls the cleanup function imported from the ./pure module inside the afterEach hook.

    We could import functions such as render from the @testing-library/react/pure module and call the cleanup function manually when needed.

    import { render } from '@testing-library/react/pure';
    import React from 'react';
    
    const Component = ({ prop1 }) => <div>{prop1}</div>;
    
    describe('Some test', () => {
      const { rerender, asFragment } = render(<Component prop1={0} />);
    
      test('Title 1', () => {
        rerender(<Component prop1={0} />);
        // logic1
        expect(asFragment().firstChild).toMatchInlineSnapshot(`
          <div>
            0
          </div>
        `);
      });
    
      test('Title 2', () => {
        rerender(<Component prop1={1} />);
        // logic2
        expect(asFragment().firstChild).toMatchInlineSnapshot(`
          <div>
            1
          </div>
        `);
      });
    });