Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

useMutation onCompleted function is not invoked in react unit testing

This is how my component look likes. Inside the component I am making a mutation and the handleOnComplete function is invoked once the mutation query is completed. This code is working fine.

function BrandingSearch() {
  const [searchList, setSearchList] = useState([]);

  const [searchQuery, { loading, error }] = useMutation(SEARCH_NO_INDEX, {
    onCompleted: handleOnComplete,
    variables: {
      rootType: 'branding',
      input: {}
    }
  });

  useEffect(() => {
    searchQuery();
  }, [])

  function handleOnComplete(res) {
    if (res && res.searchNoIndex && res.searchNoIndex.payload) {
      const payload = res.searchNoIndex.payload;
      const dataList = payload.map((item) => ({ ...item, id: item.name })); //Get requests require name instead of id
      setSearchList(dataList)
    }
  }


  const component = (
    <CardSearch
      searchList={searchList}
    />
  )

  return component;
}

export default BrandingSearch;

Below is my testcase, useEffect is being invoked in the testcase, but handleOnComplete is not being invoked. How can I fix this.

Testcase:

describe('Test BrandingSearch', () => {
  it('', async () => {
    let deleteMutationCalled = false;
    const mocks = [
      {
        request: {
          query: SEARCH_NO_INDEX,
          variables: {
            rootType: 'branding',
            input: {}
          }
        },
        result: () => {
          deleteMutationCalled = true;
          return { data:{} };
        }
      }
    ];
    let wrapper;
    act(() => {
      wrapper = create(
        <MockedProvider mocks={mocks} addTypename={false}>
          <BrandingSearch />
        </MockedProvider>
      )
    })
    let component = wrapper.root;
    expect(deleteMutationCalled).toBe(true);
    //Expecting this to be set true, once the mutation is fired.
  })
});

Any help is appreciated.

like image 906
user3587856 Avatar asked Oct 27 '25 07:10

user3587856


1 Answers

After hours of reading through articles and threads, I finally figured the issue out with some hit & trial.

Cause: As we know, the useQuery, useLazyQuery and useMutation are async calls. So when these are called, they make the API call, wait for the response, and then process the onCompleted or onError callbacks. Let's consider a useQuery hook. When we call render() in our test cases, the hook is called upon mounting but the test terminates way before the async call gets finished and the onCompleted doesn't even get the chance to execute. The test hence uses the initially added states and doesn't update based on the data we provide in the mocks.

Solution: There needs to be a gap between the call of the hook and the assertion, in order to give room to onCompleted() for getting executed. Add the async keyword before the test case and add a delay after the render(), like this:

await act(() => new Promise((resolve) => setTimeout(resolve, 2000)));

This can also be added when useLazyQuery or useMutation is called (say on the click of a button). Although this is kind of a miss from the devs end, I feel this should be highlighted somewhere in the documentation for MockedProvider.

like image 123
Akshat J. Avatar answered Oct 28 '25 21:10

Akshat J.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!