apollo-client icon indicating copy to clipboard operation
apollo-client copied to clipboard

onError is not called when using testing-library/react-hooks

Open brandon-leapyear opened this issue 5 years ago • 11 comments

Intended outcome: I am writing a custom hook using Apollo React hooks, setting onError to do console.error if an error occurs, but otherwise only dealing with successful data. I then want to test the error state of the custom hook using renderHook from @testing-library/react-hooks and MockedProvider. I would expect onError to be called if an error occurs, with the same value as the error key in the useQuery result.

Actual outcome: onError is never actually called

How to reproduce the issue: The following jest test fails on the last assertion on onErrorData

import { useQuery } from '@apollo/react-hooks'
import { MockedProvider } from '@apollo/react-testing'
import { act, renderHook } from '@testing-library/react-hooks'
import gql from 'graphql-tag'
import _ from 'lodash'
import React, { useState } from 'react'

type MyQuery = {
  name: string
}

const MyQueryDocument = gql`
  query {
    name
  }
`

const useFoo = () => {
  const [onErrorData, setOnErrorData] = useState()
  const { data, error } = useQuery<MyQuery>(MyQueryDocument, {
    onError: setOnErrorData
  })

  return { data, error, onErrorData }
}

test('onError should be called', async () => {
  const mocks = [
    {
      request: {
        query: MyQueryDocument,
      },
      error: new Error('this is an error'),
    },
  ]

  const { result } = renderHook(() => useFoo(), {
    wrapper: ({ children }) => (
      <MockedProvider mocks={mocks}>
        <>{children}</>
      </MockedProvider>
    )
  })

  await act(() => new Promise(_.defer))

  expect(result.current.data).toBeUndefined()
  expect(result.current.error?.message).toBe('Network error: this is an error')
  expect(result.current.onErrorData?.message).toBe('Network error: this is an error')
})

Versions

  System:
    OS: macOS Mojave 10.14.6
  Binaries:
    Node: 13.11.0 - /usr/local/bin/node
    Yarn: 1.22.4 - ~/leapyear/lyalpha/node_modules/.bin/yarn
    npm: 6.13.7 - /usr/local/bin/npm
  Browsers:
    Chrome: 80.0.3987.163
    Firefox: 72.0.2
    Safari: 13.1

brandon-leapyear avatar Apr 10 '20 21:04 brandon-leapyear

i am facing a similar issue, did you manage to find a fix?

waldinho avatar May 29 '20 01:05 waldinho

No, we just have a skipped test with a link to this issue :(

brandon-leapyear avatar Jun 01 '20 16:06 brandon-leapyear

same issue here

adnanfajlur avatar Jun 09 '20 09:06 adnanfajlur

I can also reproduce this

afonsoduarte avatar Jul 08 '20 13:07 afonsoduarte

+1

ghost avatar Sep 23 '20 11:09 ghost

Spent the last 3 hours ripping my hair off and finally found this. Wondering why it keeps going to onCompleted callback instead of onError. Even tried just having only onError callback, and it's still not doing it.

Any updates on this?

ardok avatar Nov 12 '20 00:11 ardok

any updates on this?

ahmadelgabrii avatar Dec 14 '20 18:12 ahmadelgabrii

any update?

This is not working also, it's not triggering the onError of useQuery

jest.spyOn(axios, 'get').mockImplementation(() => {
		return Promise.reject('error');
	});

emil45 avatar Apr 22 '21 14:04 emil45

solution found tannerlinsley/react-query#2196 (comment)

Does apollo-client also have retries? I dont recall that being a default configuration

brandonchinn178 avatar Aug 08 '21 02:08 brandonchinn178

For everyone still looking for an answer. This works for me. Make sure to NOT include the error inside of the result key in the mock.

So instead of writing:

export const mock = (errorMessage: string) => {
  return {
    request: {
      ...
    },
result: {
    errors: [new GraphQLError(errorMessage)],
},
  };
};

Turn it to:

export const mock = (errorMessage: string) => {
  return {
    request: {
      ...
    },
    errors: [new GraphQLError(errorMessage)],
  };
};

Then, and only then, your onError callback will get invoked during the tests.

Cheers! 🥳

jonathanfilbert avatar May 23 '22 10:05 jonathanfilbert

☝️ ☝️ ☝️ This may not be ideal for your test cases.

You're setting the network response error when some may want to test graphql errors

It results in an error like:

graphQLErrors: [],
networkError: GraphQLError [Object] {
  message: 'my error message',
  extensions: [Object]
},

This makes my implementation for onError not work because I want to handle graphql errors, not network errors


  function onError(error?: ApolloError) {

    if (!error?.graphQLErrors.length) {
      return;
    }

    const err = error.graphQLErrors[0];

    if (err.extensions?.code === "CONFLICT") {
      setErrors(...);
    } 

  }

zebapy avatar Aug 12 '22 17:08 zebapy

That's a good call out, @zebapy -- here's an example of a test that calls onError when a GraphQL error is returned from our Apollo Client test suite: https://github.com/apollographql/apollo-client/blob/router-subs/src/react/hooks/tests/useQuery.test.tsx#L1847:L1888

Since the original issue was opened several years ago and this seems to no longer be a problem (and, I might add, since testing-library/react-hooks is deprecated and renderHook has been moved inside of @testing-libary/react), I'm going to close this out. Thanks!

alessbell avatar Mar 08 '23 18:03 alessbell

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. For general questions, we recommend using StackOverflow or our discord server.

github-actions[bot] avatar Apr 08 '23 00:04 github-actions[bot]