Testing custom hooks is a common question as well. Step back and think about how our guiding testing principle applies to this situation: the more your tests resemble the way your software is used, the more confidence they can give you. How is your custom hook used? It’s used in a component! So that’s how it should be tested.
Often, the easiest and most straightforward way to test a custom hook is to create a component that uses it and then test that component instead.
In this exercise, we have gone back to our simple counter, except now that logic is all in a custom hook and we need to test that functionality. To do that, we’ll make a test component that uses the hook in the typical way that our hook will be used and then test that component, indirectly testing our hook.
Sometimes it’s hard to write a test component without making a pretty complicated “TestComponent.” For those situations, you can try something like this:
let resultfunction TestComponent(props) {result = useCustomHook(props)return null}// interact with and assert on results here
Learn more about this approach from my blog post: How to test custom React hooks
Add tests titled:
And test those use cases. Then abstract away the common logic into a setup
function. This one might be a little tricky thanks to variable references, but I
know you can do it!
💰 Here’s a little tip. Due to variable references, you’ll need to change your test component a bit:
const results = {}function TestComponent(props) {Object.assign(results, useCustomHook())return null}// interact with and assert on results here
Your setup
function is very similar to the renderHook
function from
@testing-library/react
!
Swap your own setup
function with that!
NOTE: Originally this exercise used
@testing-library/react-hooks
which was similar, but that functionality was merged directly into@testing-library/react
so you’re going to use that instead.
After the instruction, if you want to remember what you’ve just learned, then fill out the elaboration and feedback form:
Step back and think about how our guiding testing principle applies to this situation: the more your tests resemble the way your software is used, the more confidence they can give you. How is your custom hook used? It’s used in a component! So that’s how it should be tested.
import * as React from 'react'function useCounter({initialCount = 0, step = 1} = {}) {const [count, setCount] = React.useState(initialCount)const increment = () => setCount(c => c + step)const decrement = () => setCount(c => c - step)return {count, increment, decrement}}export default useCounter
import {renderHook, act} from '@testing-library/react'import useCounter from '../../components/use-counter'test('exposes the count and increment/decrement functions', () => {const {result} = renderHook(useCounter)expect(result.current.count).toBe(0)act(() => result.current.increment())expect(result.current.count).toBe(1)act(() => result.current.decrement())expect(result.current.count).toBe(0)})test('allows customization of the initial count', () => {const {result} = renderHook(useCounter, {initialProps: {initialCount: 3}})expect(result.current.count).toBe(3)})test('allows customization of the step', () => {const {result} = renderHook(useCounter, {initialProps: {step: 2}})expect(result.current.count).toBe(0)act(() => result.current.increment())expect(result.current.count).toBe(2)act(() => result.current.decrement())expect(result.current.count).toBe(0)})test('the step can be changed', () => {const {result, rerender} = renderHook(useCounter, {initialProps: {step: 3},})expect(result.current.count).toBe(0)act(() => result.current.increment())expect(result.current.count).toBe(3)rerender({step: 2})act(() => result.current.decrement())expect(result.current.count).toBe(1)})
Quick Links
Legal Stuff
Social Media