5 tips for debugging Apollo GraphQL MockedProvider
Tags: graphql, typescript, programming, testing
Published: 2022-06-07 13:16:51

---

description: 'We’re heavy users of Apollo GraphQL in my team at Swarmia, but sometimes mocking the queries is bothersome. This blog post is here to help!'
---

We’re heavy users of Apollo GraphQL in my team at [Swarmia](https://www.swarmia.com/), but sometimes mocking the queries is bothersome. And based on a few GitHub comments, it looks like [we’re](https://github.com/apollographql/react-apollo/issues/1711) [not](https://github.com/apollographql/react-apollo/issues/617) [alone](https://github.com/apollographql/apollo-client/issues/7134). This blog post is here to help!

## Why is debugging so difficult?

The problems arise for a few reasons:

- **Mocks lack verbosity.** It’s not easy to debug why a test case failed if the issue originates from mocks. This post will cover a solution, though, so keep reading.
- **Mocks need incredible precision.** Even a single-char difference in mocked variables vs. received variables will cause the _"No more mocked responses for the query: ...” error._
- **There’s no expected vs. actual diffing.** There’s an [old PR](https://github.com/apollographql/react-apollo/pull/2883) about this.
- **There’s no built-in way to assert if all mocks were used.** This assertion would be useful to know if a test case ended up doing what was intended. In contrast, for example, [nock](https://github.com/nock/nock) library allows examining all pending mocks to determine if the code fired all the expected requests.
- **It’s common to have multiple problems at the same time.** A multitude of things can go wrong when a test case requires mocking. It can be difficult to understand what the core reason is, and sometimes multiple issues appear in parallel. There are a variety of common issues: async UI updates, issues with Apollo mock, jest working its magic, etc. During those cases, you easily blame Apollo mocks for _everything (at least I do!)_, when in reality, there’s an overlapping issue with something else.

Let’s go through five practical tips to make debugging Apollo MockedProvider more enjoyable.

## 1. Add logging with MockLink

Queries fail silently during a test case if the `MockedProvider` doesn’t find a matching mock for the given request. You may eventually figure it out by console-logging the error returned by the `useQuery` hook, but that requires additional digging.

Fortunately, we can use [MockLink](https://github.com/apollographql/apollo-client/blob/607e32b11e433e0eef361df4a554910edd61908f/src/testing/core/mocking/mockLink.ts) to add logging. I would link to the API docs but couldn’t find any. We have a `VerboseMockedProvider` helper in our test utilities which we use as a replacement for `MockedProvider`.

```tsx
import { ApolloError, ApolloLink } from '@apollo/client'
import { onError } from '@apollo/client/link/error'
import {
  MockedProvider,
  MockedProviderProps,
  MockedResponse
} from '@apollo/client/testing'
import { MockLink } from '@apollo/react-testing'

interface Props extends MockedProviderProps {
  mocks?: ReadonlyArray<MockedResponse>
  children?: React.ReactElement
}

const VerboseMockedProvider = (props: Props) => {
  const { mocks = [], ...otherProps } = props

  const mockLink = new MockLink(mocks)
  const errorLoggingLink = onError(
    ({ graphQLErrors, networkError }) => {
      if (graphQLErrors) {
        graphQLErrors.forEach(
          ({ message, locations, path }) =>
            console.log(
              '[GraphQL error]:' +
                `Message: ${message},` +
                `Location: ${locations},` +
                `Path: ${path}`
            )
        )
      }

      if (networkError) {
        console.log(`[Network error]: ${networkError}`)
      }
    }
  )
  const link = ApolloLink.from([errorLoggingLink, mockLink])

  return (
    <MockedProvider
      {...otherProps}
      addTypename={false}
      mocks={mocks}
      link={link}
    />
  )
}
```

Using the helper, we can easily spot missing mocks from the CI logs or the local shell you’re using to run tests:

```
[Network error]: Error: No more mocked responses for the query:
query getDogParent {
  parent {
    id
    name
  }
}
Expected variables: {}
```

When seeing this error, we know that Apollo `MockProvider` received `query getDogParent` from a component but no matching mock was found. The phrase “Expected variables” can be interpreted in different ways but it means**:** “The attempted query had these variables: …”. Everything logged here refers to the attempted query, not to the mocks the MockedProvider _does_ have.

We found out about this trick from [a StackOverflow question](https://stackoverflow.com/questions/60100062/apollos-mockedprovider-doesnt-warn-clearly-about-missing-mocks), kudos to Quentin!

![StackOverflow comments](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o0jyn8n0ic9ee0el03rb.png)


## 2. **Log the `error` inside a component**

The previous tip helps to understand if an error with mocks exists in the first place. This second tip helps to dive deeper into the underlying problem.

Sometimes the additional logging within component context is the final piece of the puzzle. Let’s say you have this code in a component:

```tsx
const [setFooMutation, { loading }] = useSetFooMutation()
```

You can log the error within a component:

```tsx
const [setFooMutation, { error, loading }] =
  useSetFooMutation()
console.log('got error from mutation:', error)
```

This helps to further narrow the context of the error and allows logging additional details like component props.

## 3. Make sure the mocks match the queries made by a component _exactly_

`MockLink` is responsible for checking if a request is mocked. The [logic](https://github.com/apollographql/apollo-client/blob/607e32b11e433e0eef361df4a554910edd61908f/src/testing/core/mocking/mockLink.ts#L76-L91) it uses to detect matching mocks is pretty unforgiving. In practice, everything needs to match exactly:

- **Query:** does the attempted GraphQL query match the mocked query? Even a single character typo will cause a mismatch.
- **Variables:** does the attempted query have the exact same variables? If the attempted query variables contain an array, the items need to be equal and even in the same order in the mock.
- **The number of mocks:** does each attempted query have a corresponding mock? Even if you are doing _the same query_ three times, you need three separate mock definitions in the `mocks` array prop. Each attempted request that matches a mock, [consumes](https://github.com/apollographql/apollo-client/blob/607e32b11e433e0eef361df4a554910edd61908f/src/testing/core/mocking/mockLink.ts#L106) the mocks that are left.

Let’s say we have a mock like this:

```tsx
import { SetExcludedPullRequestsDocument } from 'src/generated/graphql'

function mockSetExcludedPullRequests(ids: number[]) {
  return {
    request: {
      query: SetExcludedPullRequestsDocument,
      variables: { ids }
    },
    result: () => {
      return {
        data: {
          setExcludedPullRequests: ids
        }
      }
    }
  }
}
```

We then mock the query in a test case:

```tsx
<VerboseMockedProvider
  mocks={[mockSetExcludedPullRequests([10, 11, 14])]}
>
```

And eventually the component calls the query with IDs in the wrong order:

```tsx
await setExcludedPullRequestsMutation({
  variables: { ids: [11, 10, 14] }
})
```

Boom. That little slip caused a _“No more mocked responses for the query: ...”_ error. The same would happen if the component had accidentally used `id` variable instead of `ids`. The same would also happen if we’d called `setExcludedPullRequestsMutation` twice, when having only a single mock definition. The possibilities for mistakes are endless.

The only solution is to make sure the mocked query has the _exact_ same query and variables as what the component attempts to send. Sometimes you need a magnifying glass and diffing tools to spot these.

My preferred debugging approach is to log the mock query object _(i.e. what `mockSetExcludedPullRequests` returns)_, copy paste it to an editor and manually compare the mock request to the attempted query. If you’re brave enough, there’s a [fork](https://github.com/apollographql/react-apollo/pull/2883#issuecomment-644907868) of `@apollo/react-testing` which implements a jest-like diffing output for query variables.

## 4. Make sure you mock the _correct_ query

This one sounds obvious but when you’re setting the mocks for `MockedProvider`, make sure you’re using the correct mocks. The tip originates from a real issue that I once hit which caused a few hours of waste.

We have shared query mock creator utilities to make them reusable across test suites, for example `mockGetSomething()` and `mockSetSomething()`. The setter mock creator took zero parameters, so TypeScript didn’t complain about it either.

```tsx
<VerboseMockedProvider
  mocks={[
    mockGetSomething(),
    // The next item should've mocked the *setter*,
    // not the getter
    mockGetSomething()
  ]}
>
```

There’s nothing wrong about the creator utility pattern, the lesson here is to be more careful with the mock definitions.

One way to make sure that you’re calling the correct mock is to add logging to the result creator function:

```tsx
import { SetSomethingDocument } from 'src/generated/graphql'

function mockSetSomething() {
  return {
    request: {
      query: SetSomethingDocument
    },
    result: () => {
      console.log('-- THIS SHOULD BE CALLED ')
      return {
        data: {
          setSomething: true
        }
      }
    }
  }
}
```

If you don’t see the log output in stdout, then you know something weird is happening.

## 5. Make sure only one `MockedProvider` is mounted

If multiple `MockedProvider` components are mounted at the same time, the one that is deepest in the React component tree will be used for queries. This is an easy pitfall to step into when abstracting the numerous React context providers into utilities.

We often use helper components to reuse the set of providers that are required in all test cases. A test suite sometimes uses the same data and mocks for each individual case to avoid repetition. The helper component could look like this:

```tsx
const Providers = ({ children }: ProviderProps) => (
  <MemoryRouter>
    {/* Mock requests relevant for the test cases */}
    <VerboseMockedProvider mocks={[mockGetSomething()]}>
      {children}
    </VerboseMockedProvider>
  </MemoryRouter>
)
```

Then, the actual test cases don’t need to repeat all the providers:

```tsx
render(
  <Providers>
    <TestComponent />
  </Providers>
)
```

But sometimes, one might accidentally use two or more overlapping provider abstractions. This leads to mounting multiple `MockedProvider` components at the same time. This is what a simplified example would look like:

```tsx
render(
  <Providers>
    <VerboseMockedProvider mocks={[mockGetAnotherThing()]}>
      <TestComponent />
    <VerboseMockedProvider>
  </Providers>
)
```

When the `TestComponent` attempts to call `query getSomething`, it will fail to _“No more mocked responses…”_ error because the inner `VerboseMockedProvider` catches all requests before they reach the outer provider defined in `Providers` component.

The simplest solution is to read through the whole React component tree in the test code and make sure there are no overlapping `MockedProvider` components. An additional safety layer can be achieved by [disallowing](https://eslint.org/docs/rules/no-restricted-imports) direct `MockedProvider` imports using ESLint.

## That’s it

If you have any additional tips for Apollo mocking, let us know on [Twitter](https://twitter.com/SwarmiaHQ). Hope you found the tips useful!