Mocking React hooks for testing with Jest and react-testing-library

Image for post
Image for post
Obligatory hook-related stock photo (📷 by Chunlea)

Imagine this familiar scenario: a developer builds a life-changing todo application using React and has decided to leverage hooks to make the sharing of state logic much cleaner. Being a responsible developer with a passion for testing, they bring in jest and react-testing-library to start writing unit and integration tests for their components.

Upon implementing these component tests, a challenge arises:
How does one mock a hook’s value when its state exists outside the scope of a component?

To help solve this, we can leverage jest.mock()

Mocking our hook state with a dynamic variable

Imagine that our component relies on a useAuth() hook to share state about the user’s logged in status.

// App.tsxconst App = () => {
const { isLoggedIn } = useAuth()
const text = isLoggedIn ? 'Welcome' : 'Login'
return (
<Text>{text}</Text>
)
}

At the top of our component test, we can add a jest.mock() that includes a relative path to where our hook module is defined. We can also provide a dynamic variable called mockIsLoggedIn, that we can mutate in each test to simulate different hook states.

Note: due to a requirement by jest, it is important to begin this dynamic variable with the prefix of “mock” (e.g. mockIsAdmin, mockIsCustomer).

// App.test.tsximport App from './'
import { render } from '@testing-library/react'
let mockIsLoggedIn = falsejest.mock('../hooks/use-auth', () => {
return jest.fn(() => {
isLoggedIn: mockIsLoggedIn
})
})
test('can show logged in message', () => {
mockIsLoggedIn = true
const { getByText } = render(<App/>)
expect(getByText('Welcome')).toBeTruthy()
})

Mocking that a hook method was called

In the component below, we provide the ability to sign out through our useAuth() hook. It would be great if we can test that this hook can be called on pressing of the sign out button.

// SignOut.tsxconst SignOut = () => {
const { signOut } = useAuth()
return (
<Button data-testid='sign-out' onClick={() => signOut()}>
Sign Out
</Button>
)
}

As with our first example, we can use jest.mock to mock the useAuth() hook module and then add a mock function that we can use to assert things in our tests.

// SignOut.test.tsximport SignOut './'
import { render } from '@testing-library/react'
const mockSignOutFn = jest.fn()jest.mock('../hooks/use-auth', () => {
return jest.fn(() => {
signOut: mockSignOutFn
})
})
test('can sign out', () => {
const { getByTestId } = render(<SignOut/>)
fireEvent.click(getByTestId('sign-out'))
expect(mockSignOutFn).toBeCalled()
})

Other helpful jest methods to consider using here are toBeCalledWith() and toHaveBeenCalledTimes().

With jest mocking, we can get specific with our test assertions while using React hooks in our components. Above are some examples of how we can do this to help us build deeper test coverage 😃.

Written by

Software Developer from Canada 🇨🇦

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store