Unit testing Apollo React hooks with MockedProvider

Published on 16 November 2019

This article describes how to unit test a React component with Apollo queries using React Hooks, Jest, the Apollo-provided MockedProvider, and React Testing Library.

When set up, MockedProvider asserts that any requests that are passed in its setup are called. That is, if you pass in that certain queries are going to be called with certain variables, the test will fail if it’s not called in exactly the same way.

Wrapper library (without Redux)

import { MockedProvider, MockedResponse } from '@apollo/react-testing';
import React from 'react';

interface TestConfig {
  apolloMocks?: MockedResponse[];
}

export const ApolloWrapper = ({
  apolloMocks = [],
}: TestConfig) => ({ children }: any) => {
  return (
    <MockedProvider mocks={apolloMocks}>{children}</MockedProvider>
  );
};

Wrapper library (with Redux)

Redux is commonly used with react, and one can also set up a provider with a store. Note that we use typesafe-actions in this example.

/*
 * These Redux functions should probably be in a different, production library,
 * but they're included here for illustration.
 */
import { combineReducers, createStore, Store } from 'redux';
import { StateType } from 'typesafe-actions';
import { myReducer } from 'path/to/my/reducer';

const rootReducer = combineReducers({
  reducer: myReducer,
});

export type RootState = StateType<typeof rootReducer>;

const makeStore = (initialState = {}): Store<RootState> => {
  return createStore(rootReducer, initialState);
};

// Test wrapper code.
import { MockedProvider, MockedResponse } from '@apollo/react-testing';
import React from 'react';
import { Provider } from 'react-redux';
import { Store } from 'redux';
import makeStore, { RootState } from './store';

interface TestConfig {
  initialState?: Partial<RootState>;
  store?: Store<RootState>;
  apolloMocks?: MockedResponse[];
}

export const ReduxApolloWrapper = ({
  initialState = {},
  store = makeStore(initialState),
  apolloMocks = [],
}: TestConfig) => ({ children }: any) => {
  return (
    <Provider store={store}>
      <MockedProvider mocks={apolloMocks}>{children}</MockedProvider>
    </Provider>
  );
};

Test example

This is an example of a test written using Jest.

import gql from 'graphql-tag';

test('does what it should', async () => {
  const mocks = [
    {
      request: {
        query: gql`<your expected query here>`,
        variables: {
          myVariable: 'foo',
        }
      },
      result: {
        data: {
          // Mock out the return response here. You can also have `result`
          // return a function if you want to do more complicated things.
        },
      },
    },
  ];


  const { getByText } = render(
    <MyComponentWithApolloQuery  />,
    {
      wrapper: ReduxApolloWrapper({ apolloMocks: MOCKS }),
    }
  );

  expect(getByText(/someSubstringRegex/)).not.toBeVisible();

  // This test will implicitly fail here if Apollo is called, for example, with
  // a variable value for `myVariable` other than `foo`.
});



Errors and warnings

Client not in context

For some reason, MockedProvider would not work properly for me in React Testing Library the way that was suggested by the documentation. I kept getting this error:

Invariant Violation: Could not find "client" in the context or passed in as an
option. Wrap the root component in an <ApolloProvider>, or pass an ApolloClient
instance in via options.

In the end, this Spectrum thread helped me to resolve the issue.

Add typename warnings

I never really figured out how to solve these when I was using GraphQL codegen, as the fragments that are generated don’t contain typenames. You’ll likely see warning below, for which I’ve currently not solved this issue (though there are some solutions proposed here).

You're using fragments in your queries, but either don't have the addTypename:
        true option set in Apollo Client, or you are trying to write a fragment to the store without the __typename.
         Please turn on the addTypename option and include __typename when writing fragments so that Apollo Client
         can accurately match fragments.

Comments