React: mock hooks in unit tests
Recently I came across the situation where I built a custom hook to check for some keycloak rules that used
the useKeycloak
hook from the @react-keycloak/ssr
library. As you can imagine, the library
does all sort of stuff with cookies, local storage, etc. to provide the data from the browser session, but I don't know
or care about implementation details, and neither should you. Unit tests should always isolate as much side effects as
possible and so I set out to write a test which can mock the useKeycloak
hook.
Fortunately jest provides us with the spyOn
function to mock functions (after all to the javascript runtime, hook are only functions),
without the need to modify the hooks to be testable (with one minor exception, more on that later.
So let's create a simple example that demonstrates the principle. We have two hooks, useExternal
, which we want to
test and useInternal
which is used by useExternal
and can do anything from accessing the browser session to fetching
data from an api, which we want to mock in our tests. For this example though it only returns a number
useExternal.ts
export const useInternal = ()=>{
return -1
}
useInternal.ts
import {useInternal} from "./useInternal";
export const useExternal = ()=>{
const number = useInternal()
return number *2
}
As stated earlier, there is one important detail: the two hooks need to be in two different files, this is due the way
how spyOn
works. You can use jest.spyOn(require('./module'), 'exportedMember')
to tell jest to replace the
export exportedMember
from the file module.ts
, but only if that function gets imported somewhere, so keep that in mind.
The whole test can look something like this:
useExternal.spec.ts
import {useExternal} from "./useExternal";
const useInternal = jest.spyOn(require('./useInternal'), 'useInternal')
useInternal.mockReturnValue(2)
test('useExternal', ()=>{
const actual = useExternal()
const expected = 4
expect(actual).toBe(expected)
})
And after running the test you will see that the mocked value 2
got used and the result is indeed 2*2=4
You can also find the code on github