Is there a way to mock refetch in MockedProvider - Apollo Client?
Asked Answered
A

5

5

Here is how I am using MockedProvider. How can I mock refetch in mocks array?

const mocks = [{
        request: {
            query: GET_USERS_BY_FACILITY,
            variables: {
                facility: 300
            }
        },
        result: {
            data: {
                GetUsersByFacility: [{
                    nuId: 'Q916983',
                    userName: faker.internet.userName(),
                    profileKey: 'testKey',
                    profileValue: 'testValue',
                    __typename: 'FacilityUser'
                }]
            }
        },
        refetch: () => {
            return {
                data: {
                    GetUsersByFacility: [{
                        nuId: 'Q916983',
                        userName: faker.internet.userName(),
                        profileKey: 'testKey',
                        profileValue: 'testValue',
                        __typename: 'FacilityUser'
                    }]
                }
            }
        }
    }

This test case calls refetch function when delete event is triggered.

 it('should be able to click on delete user', async () => {
        const {getByTestId} = render(
            <MockedProvider mocks={mocks}>
                <Users selectedFacility={300}/>
            </MockedProvider>)

        await wait(0)
        fireEvent.click(getByTestId('btnDelete'))
    })

I have been trying different ways, none seems to work. I get error message as TypeError: Cannot read property 'refetch' of undefined.

Thank you very much in hope of an answer.

Regards, --Rajani

Acicula answered 21/11, 2019 at 18:13 Comment(3)
I don't see where you're calling refetch.Illusive
refetch is called in "Users" component when click on delete button using 'fireEvent'.Acicula
Can you post that code? Is it working with a regular Apollo Provider?Illusive
B
3

Maybe it's a bit late to answer, but if you have't got any answers yet, you would refer to the way I solved.

Please note that this might not be the correct answer.


const mocks = [
  {
    request: {
      query: GET_DOG_QUERY,
      variables: {
        name: 'Buck',
      },
    },
    result: () => {
      // do something, such as recording that this function has been called
      // ...
      return {
        data: {
          dog: { id: '1', name: 'Buck', breed: 'bulldog' },
        },
      }
    },
  },
];
  • I make my refetch testcode based on this phrase // do something, such as recording that this function has been called

This is my mock example.

let queryCalled = false

const testingData = (value) => ({
  data: {....}
})

const TESTING_MOCK = {
  request: {
    query: TESTING_QUERY,
    variables: { some: "variables" },
  },
  result: () => {
    if (queryCalled) return testingData("refetched");
    else {
      queryCalled = true;
      return testingData("first fetched");
    }
  },
};
  • This component refetches data when the button is clicked. I designed my test code in this order
    1. when it's rendered for the first time, it fetches the mock data .
      => In the code above, queryCalled is false so, it reassigns queryCalled as true and return the "first fetched" mock data,
    2. when a click event occurs, refetch occurs too.
      => On the same principle the mock data returns "refetched" mock data.

My testcode example is here

it("refetch when clicked save button.", async () => {
  const mocks = [TESTING_MOCK];
  let utils: RenderResult = render(<SomeTestingComponent mocks={mocks} />);

  await waitForNextTick(); //for getting a data, not loading

  const titleInput = utils.getByDisplayValue("first fetched");

  const saveBtn = utils.getByText("save");
  fireEvent.click(saveBtn);
  await waitForElement(() => utils.getByDisplayValue("refetched"));
})

Please let me know if you have any other suggestions!

Bullington answered 21/1, 2020 at 5:44 Comment(0)
G
2

For anyone that might still run into this, the solution to make refetch work in your tests is to use the newData method while keeping track of the query having been called.

I don't know if this is a bug in the MockedProvider implementation, but I was banging my head against the wall trying to make newData work together with result, but it turns out that newData completely overrides result.

A working solution (tested with useQuery and Apollo Client 3) would be something like this:

let queryCalled = false;
const refetchMock = {
  request: {
    query: YOUR_QUERY
  },
  newData: () => {
    if (queryCalled) {
      return { 
        data: {
          // your refetched data
        }
      }; 
    } else {
      queryCalled = true;
      return { 
        data: {
          // your first fetched data
        } 
      }; 
    }
  }
};
Gangboard answered 30/11, 2020 at 15:51 Comment(0)
J
1

The newData solution didn't work for me with apollo client @2.6.

As a workaround, for the few tests that utilize refetch I had to physically mock the useQuery function and provide mock functions for the return of refetch; for our custom hook (where an overridden useQuery hook is exported as default), it looked something like this:

import * as useQueryModule from '~/hooks/useQuery';

describe('test the thing', () => {
    let useQuerySpy;

    beforeEach(() => {
        // Spy on the `useQuery` function so we can optionally provide alternate behaviour.
        useQuerySpy = jest.spyOn(useQueryModule, 'default');
    })

    afterEach(() => {
      // Restore any mocked behaviour
      useQuerySpy.mockRestore();
    });

    it('does a thing', async () => {
        const refetchedApolloResponse = buildResponse(refetchData) // some function to build the shape / data of apollo response
        const initialApolloResponse = buildResponse(initialData) // some function to build the shape / data of apollo response
     
       const mockRefetch = jest.fn().mockResolvedValue({ data: refetchedApolloResponse });
       useQuerySpy.mockReturnValue({ data: initialApolloResponse, refetch: mockRefetch });

      // Assert stuff
    }
})

Jandel answered 5/11, 2020 at 13:57 Comment(0)
C
0

This solution did not work for me and not sure whether it will work or not because it didn't work in my case.

let queryCalled = false

const testingData = (value) => ({
  data: {....}
})

const TESTING_MOCK = {
  request: {
    query: TESTING_QUERY,
    variables: { some: "variables" },
  },
  result: () => {
    if (queryCalled) return testingData("refetched");
    else {
      queryCalled = true;
      return testingData("first fetched");
    }
  },
};

So I solved it by another way which is:

const mocks = [
  {
    request: {
      query: GET_DOG_QUERY,
      variables: {
        name: 'Buck',
      },
    },
    result: {
      data: {
        dog: { id: '1', name: 'Buck', breed: 'bulldog' },
      },
    },
    newData: jest.fn(() => ({
      data: {
        dog: { id: '1', name: 'Refetched-Buck', breed: 'refetched-bulldog' },
      },
    })),
  },
];

It worked like a charm for me.

Cleavage answered 24/2, 2020 at 16:22 Comment(0)
K
0

For anyone else that stumbles upon this. The straightforward way to fix this is to just have two different responses for the different variables passed. Eg.

const mocks = [
  {
    request: {
      query: GET_DOG_QUERY,
      variables: {
        name: 'Buck',
      },
    },
    result: {
      data: {
        dog: { id: '1', name: 'Buck', breed: 'bulldog' },
      },
    },
  },
  {
    request: {
      query: GET_DOG_QUERY,
      variables: {
        name: 'Buckey', // new variable
      },
    },
    result: {
      data: {
        dog: { id: '1', name: 'Buckey', breed: 'bulldog' },
      },
    },
  },
];

depending on what your input field is passing as a variable

Karie answered 3/10, 2024 at 3:43 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.