reactjsreact-hooksjestjsreact-testing-library

Should I manually call unmount of renderHook in react-testing-library?


React Testing library docs say it's not needed to do cleanups after every test, but for the renderHook docs don't say anything if unmount is called after test ends.

So is it necessary to call unmount in tests with renderHook?


Solution

  • You don't need to call RTL cleanup function when you use renderHook(), from the source code, we know it calls render function underly with a <TestComponent/>. So

    cleanup is called after each test automatically by default if the testing framework you're using supports the afterEach global (like mocha, Jest, and Jasmine).

    function renderHook(renderCallback, options = {}) {
      const {initialProps, ...renderOptions} = options
    
      if (renderOptions.legacyRoot && typeof ReactDOM.render !== 'function') {
        const error = new Error(
          '`legacyRoot: true` is not supported in this version of React. ' +
            'If your app runs React 19 or later, you should remove this flag. ' +
            'If your app runs React 18 or earlier, visit https://react.dev/blog/2022/03/08/react-18-upgrade-guide for upgrade instructions.',
        )
        Error.captureStackTrace(error, renderHook)
        throw error
      }
    
      const result = React.createRef()
    
      function TestComponent({renderCallbackProps}) {
        const pendingResult = renderCallback(renderCallbackProps)
    
        React.useEffect(() => {
          result.current = pendingResult
        })
    
        return null
      }
    
      const {rerender: baseRerender, unmount} = render(
        <TestComponent renderCallbackProps={initialProps} />,
        renderOptions,
      )
    
      function rerender(rerenderCallbackProps) {
        return baseRerender(
          <TestComponent renderCallbackProps={rerenderCallbackProps} />,
        )
      }
    
      return {result, rerender, unmount}
    }
    

    If you want to test the code logic in cleanup function of useEffect() hook, you should call ummount() function. See below example:

    import { renderHook, screen } from '@testing-library/react';
    import React, { useEffect } from 'react';
    
    describe('78435539', () => {
      test('should pass', () => {
        const Context = React.createContext('default');
        function Wrapper({ children }) {
          return (
            <Context.Provider value="provided">
              <h1>Provider 1</h1>
              {children}
            </Context.Provider>
          );
        }
        const { result } = renderHook(() => React.useContext(Context), { wrapper: Wrapper });
        expect(result.current).toEqual('provided');
        screen.debug();
      });
    
      test('should pass 2', () => {
        const Context = React.createContext('default');
        function Wrapper({ children }) {
          return (
            <Context.Provider value="provided">
              <h1>Provider 2</h1>
              {children}
            </Context.Provider>
          );
        }
        const { result } = renderHook(() => React.useContext(Context), { wrapper: Wrapper });
        expect(result.current).toEqual('provided');
        screen.debug();
      });
    
      test('should pass 3', () => {
        let count = 0;
        const { unmount } = renderHook(() => {
          useEffect(() => {
            const onClick = () => {
              count++;
            };
            document.body.addEventListener('click', onClick);
            return () => {
              document.body.removeEventListener('click', onClick);
            };
          }, []);
        });
    
        document.body.click();
        expect(count).toBe(1);
        unmount();
        document.body.click();
        expect(count).toBe(1);
      });
    });
    

    Test result:

    > jest -o
    
      console.log
        <body>
          <div>
            <h1>
              Provider 1
            </h1>
          </div>
        </body>
    
          at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:87:13)
    
      console.log
        <body>
          <div>
            <h1>
              Provider 2
            </h1>
          </div>
        </body>
    
          at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:87:13)
    
     PASS  stackoverflow/78435539/index.test.tsx
      78435539
        √ should pass (32 ms)
        √ should pass 2 (5 ms)
        √ should pass 3 (2 ms)
    
    Test Suites: 1 passed, 1 total
    Tests:       3 passed, 3 total
    Snapshots:   0 total
    Time:        1.044 s
    Ran all test suites related to changed files.