Photo by Anna Hunko on Unsplash
Hooks are popular, as they bring readability and maintainability. Custom hooks become enablers for composability and reusability. In headless UI components, we explored custom hooks, along with high-order components (HOCs).
How can we test them? In Test Cases and Test Coverage for High Order Components, we gave examples on how to write test cases for high order components and how to measure test coverage, using the Jest and React Testing Library. In this article, we are going to demonstrate how to use React Hooks Testing Library to test custom hooks.
There are two packages needed for testing custom hooks:
npm install --save-dev @testing-library/react-hooks
npm install --save-dev react-test-renderer
After the installation, these packages become [devDependencies](https://medium.com/better-programming/package-jsons-dependencies-in-depth-a1f0637a3129)
in package.json
.
Test Renderer (react-test-renderer
) is used to render React components to pure JavaScript objects, without depending on the DOM or a native mobile environment. The installed version should match the React version.
@testing-library/react-hooks
is built on top of Test Renderer.
You probably need other @testing-library
packages too. If you use Create React App, they are part of dependencies
in package.json
:
"dependencies": {
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1"
}
renderHook
API is the center piece of @testing-library/react-hooks
. It renders a test component that calls the provided callback
, including any hooks it calls, every time it renders.
function renderHook(
callback: function(props?: any): any,
options?: RenderHookOptions
): RenderHookResult
options
has the type of RenderHookOptions
, which is an object that optionally includes:
initialProps
: It is the initial value to the callback
function of renderHook
.wrapper
: It is a React component to wrap the test component while using global context.RenderHookResult
is a data structure defined as follows:
{
current: any,
error: Error
}
We are using examples to show how renderHook
is used in various cases. Create React App is used as the working environment. All custom hooks are hosted in src/hooks
, and all hook tests are hosted in src/hooks.test.js
.
In Everyone Can Build a Custom Hook, we built a series of hooks. We are going to use them to build test cases.
This is the simplest hook:
The following are test cases:
At line 6, renderHook
is used to render the custom hook, useMyName
.
At line 7, result.current
is the return value of useMyName
.
Comparing to the case at lines 5 - 8, lines 10-13 show a different case with the initial value set to “Larry”
.
As we have explained in the other article, the following command is used to execute the test cases:
npm test -- --testMatch="<rootDir>/src/hooks.test.js" --collectCoverage --collectCoverageFrom="src/hooks.js"
Here are the test results and test coverage:
rerender
APIIn the previous tests, we repeated two tests with different initial values. This can be combined by passing the initial value to renderHook
’s callback
:
renderHook((initialName) => useMyName(initialName)
When the initial value changes, we call rerender
to trigger the hook to be recalculated.
function rerender(newProps?: any): void
Here is the alternative test suite:
This produces the same results:
Now, let’s take a look at a more complicated custom hook, which returns a method and a value.
Here are the test cases:
At line 6, renderHook
is used to render the custom hook, useMyName
. This time, current
is destructured to a method and a value (line 12). The method is tested at line 13, and the message is tested at line 14 and line 16.
Run the test suite:
#react-hook #reactjs #react #programming #javascript