There is a custom hook called useUserInfo
that wraps useQuery
from tanstack-query
, and It fetches user information.
I want to test the useAccountMenu
custom hook that uses that useUserInfo
(each role of custom hooks will be explained later).
The return value of useUserInfo
, that will be stored in data
property of returend object (convention of tanstack-query
), has the following type.
// type.ts
type ActiveUserInfo = {
id: number,
name: string,
age: number,
}
And this is the implementation of the useUserInfo
custom hook that wraps the useQuery
.
// useUserInfo.ts
export const useUserInfo = () => {
// assume fetchLoggedInUser is simple fetch()
return useQuery(['loggedInUser'], fetchLoggedInUser);
};
And this is the useAccountMenu
custom hook, which uses the useUserInfo
custom hook to return data for the account menu link.
The return value of useAccountMenu
changes depending on the value returned from useUserInfo
.
// useAccountMenu.ts
export const useAccountMenu = () => {
const {data} = useUserInfo();
if (data.id > 100) { // this is example condition
return {
menuItems: [
{menuLabel: 'User Setting', to: 'setting/user'},
{menuLabel: 'Language', to: 'setting/language'},
]
};
};
return {
menuItems: [
{menuLabel: 'User Setting', to: 'setting/user'},
{menuLabel: 'Language', to: 'setting/language'},
{menuLabel: 'Organization', to: 'setting/organization'},
]
};
};
// useAccountMenu.test.ts
jest.mock('./useAccountMenu');
describe.concurrent('useAccountMenu', () => {
it('returns all menu objects', async () => {
const mockData = {
id: 3,
name: 'John',
age: 25,
};
jest.mocked(useUserInfo).mockReturnValueOnce({ data: mockData }); // type Error!!
const { result } = renderHook(() => useAccountMenu(), { wrapper });
await waitFor(() => {
expect(result.current.menuItems).toEqual([
{menuLabel: 'User Setting', to: 'setting/user'},
{menuLabel: 'Language', to: 'setting/language'},
{menuLabel: 'Organization', to: 'setting/organization'},
]);
});
});
// and here below will be test for the case of data.id > 100.
// ...
})
The line mockReturnValueOnce
is executed, I got a TypeError: Type '{ data: { id: string; name: string; age: number; }; }' is missing the following properties from type 'QueryObserverSuccessResult<ActiveUserInfo, Error>': error, isError, isPending, isLoading, and 19 more.ts(2345)
.
In this case, do I have to hardcode isError
, isPending
, etc. every time and create mock data?
Or is there a smarter solution or a different way of testing for useAccountMenu
?
If this error can be fixed, it will relieve me of the stress I have been suffering from the last few days...
Thank you very much for your time.
The usual recommendation to test react query is to test a component that uses the hook. This is usually better because it tests what the user is seeing, not the implementation of the hook. So you can navigate to a page and see if the data is displayed correctly. This is also how all react-query tests are written internally.
However, that still means the component that uses the hook will make a network request, which you likely don't want to do in tests. Two solutions come to mind for this:
You can let the fetch
happen and intercept it with tools like msw
or nock
and return a response that fits your structure.
Before rendering your component, you can call queryClient.setQueryData(key, data)
to put some data into the cache before rendering. If you have a staleTime
set, there will be no further request happening. You can also set a larger staleTime
only for the queryClient
that you are using in the test.