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
?
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 theafterEach
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.