skip to Main Content

How would I write a passing test for the following? Where getLifestages is a working get API call that returns an array such as:

[{_id: 1, title: 'School'},{_id: 2, title: 'Work'}]

import { useQuery } from 'react-query';

const useLifestages = () => useQuery(
    ['GET_LIFESTAGES'],
    getLifestages,
    {
        staleTime: params.staleTime,
        keepPreviousData: true
    }
);

const LandingScreen = () => {
  const { data: lifestages, isLoading } = useLifestages();

  if (isLoading) return <Loading />

  return (
    <ErrorBoundary>
      <div className="container">
        <header className="header">
          Heading
        </header>
        <input 
          className="search-bar" 
          type='search' 
          role='search' 
        />
        
        {lifestages.map(lifestage => (
          <div
            key={lifestage._id}
            lifestage={lifestage} 
            role='button'
          />
        ))}

      </div>
    </ErrorBoundary>
  );
};

I want to mock the API response and what I’ve done so far hasn’t worked.

jest.mock('react-query', () => ({
  useQuery: jest.fn().mockReturnValue(({ 
     data: [{
         _id: 1, title: 'School'
       },{
         _id: 2, title: 'Work'
     }], 
     isLoading: false
}));

describe("Load landing screen", () => {
  
    it("Renders the buttons", () => {
       render(
         <LandingScreen />
       );
       const button = screen.getByRole('button');
       expect(button).toBeInTheDocument();
    })

});

The error I’m seeing is this:

TypeError: Cannot destructure property 'data' of '(0 , _queries.useLifestages)(...)' as it is undefined.

       8 |
       9 |
    > 10 |   const { data: lifestages, isLoading } = useLifestages();

It seems to me that perhaps it’s because the data hasn’t returned from the call yet and is giving undefined. But clearly my mock isn’t working as it shouldn’t be calling the API at all ideally and instead using my mocked data. I’m a little confused as I’m new to testing async code so any help will be much appreciated!

2

Answers


  1. I ran into a simliar error al while back, what i did was instead of using a mock I spied on the ReactQuery; like this:

    jest
          .spyOn(ReactQuery, 'useQuery')
          .mockImplementation(
            jest
              .fn()
              .mockReturnValue({ data: { ...SomeMockData }, isLoading: false, isSuccess: true, isError: false })
          )
    

    Hope it helps.

    Login or Signup to reply.
  2. jest.mock('react-query', () => ({
      useQuery: jest.fn().mockReturnValue(({ 
         data: [{
             _id: 1, title: 'School'
           },{
             _id: 2, title: 'Work'
         }], 
         isLoading: false
    }));
    

    This mocks the whole react-query library and just gives it a useQuery hook. I’m thinking that this won’t work because other vital parts are then missing. It’s like mocking React and only giving it a useEffect hook …

    I think you would be much better off not mocking 3rd party libraries, but mocking what you have control over, which is either how the QueryFunction is implemented, or, by just mocking the network layer.

    Mocking the network layer is what is recommended in the docs (e.g. with nock) and in my blog (e.g. with msw)

    Advantages of mocking the network layer is that you’ll not only test if your component will work if useQuery returns data, but also, what happens if it returns a loading state. By mocking useQuery, this implementation would pass the test:

    function LandingScreen() {
      const data = useQuery(...)
      
      return query.data.map(...)
    }
    

    but it would certainly die at runtime.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search