Mocking ApolloClient's client.query method with Jest
Asked Answered
M

2

8

Update January 22nd 2020

The solution from @slideshowp2 is correct, but I could not get it to work at all, due to this TypeError:

TypeError: Cannot read property 'query' of undefined

Well it turned out to be my jest configuration that had resetMocks: true set. After I removed it, the test did pass. (I don't know why though)


Original question:

I need to execute a graphql query in a helper function outside of a React component using Apollo Client and after a bit of trial and error I went for this approach which is working as it is supposed to:

setup.ts

export const setupApi = (): ApolloClient<any> => {
  setupServiceApi(API_CONFIG)
  return createServiceApolloClient({ uri: `${API_HOST}${API_PATH}` })
}

getAssetIdFromService.ts

import { setupApi } from '../api/setup'

const client = setupApi()

export const GET_ASSET_ID = gql`
  query getAssetByExternalId($externalId: String!) {
    assetId: getAssetId(externalId: $externalId) {
      id
    }
  }
`

export const getAssetIdFromService = async (externalId: string) => {
  return await client.query({
    query: GET_ASSET_ID,
    variables: { externalId },
  })

  return { data, errors, loading }
}

Now I am trying to write test tests for the getAssetIdFromService function, but I have trouble figuring out how to get the client.query method to work in tests.

I have tried the approach below including many others that did not work. For this particular setup, jest throws

TypeError: client.query is not a function

import { setupApi } from '../../api/setup'
import { getAssetIdFromService } from '../getAssetIdFromService'

jest.mock('../../api/setup', () => ({
  setupApi: () => jest.fn(),
}))

describe('getAssetIdFromService', () => {
  it('returns an assetId when passed an externalId and the asset exists in the service', async () => {
    const { data, errors, loading } = await getAssetIdFromService('e1')

    // Do assertions  
  })
}

I assume I am missing something in relation to this part:

jest.mock('../../api/setup', () => ({
  setupApi: () => jest.fn(),
}))

...but I cannot see it.

Moccasin answered 20/1, 2020 at 19:23 Comment(0)
T
12

You didn't mock correctly. Here is the correct way:

getAssetIdFromService.ts:

import { setupApi } from './setup';
import { gql } from 'apollo-server';

const client = setupApi();

export const GET_ASSET_ID = gql`
  query getAssetByExternalId($externalId: String!) {
    assetId: getAssetId(externalId: $externalId) {
      id
    }
  }
`;

export const getAssetIdFromService = async (externalId: string) => {
  return await client.query({
    query: GET_ASSET_ID,
    variables: { externalId },
  });
};

setup.ts:

export const setupApi = (): any => {};

getAssetIdFromService.test.ts:

import { getAssetIdFromService, GET_ASSET_ID } from './getAssetIdFromService';
import { setupApi } from './setup';

jest.mock('./setup.ts', () => {
  const mApolloClient = { query: jest.fn() };
  return { setupApi: jest.fn(() => mApolloClient) };
});

describe('59829676', () => {
  it('should query and return data', async () => {
    const client = setupApi();
    const mGraphQLResponse = { data: {}, loading: false, errors: [] };
    client.query.mockResolvedValueOnce(mGraphQLResponse);
    const { data, loading, errors } = await getAssetIdFromService('e1');
    expect(client.query).toBeCalledWith({ query: GET_ASSET_ID, variables: { externalId: 'e1' } });
    expect(data).toEqual({});
    expect(loading).toBeFalsy();
    expect(errors).toEqual([]);
  });
});

Unit test results with 100% coverage:

 PASS   apollo-graphql-tutorial  src/stackoverflow/59829676/getAssetIdFromService.test.ts (8.161s)
  59829676
    ✓ should query and return data (7ms)

--------------------------|----------|----------|----------|----------|-------------------|
File                      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
--------------------------|----------|----------|----------|----------|-------------------|
All files                 |      100 |      100 |      100 |      100 |                   |
 getAssetIdFromService.ts |      100 |      100 |      100 |      100 |                   |
--------------------------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        8.479s

Source code: https://github.com/mrdulin/apollo-graphql-tutorial/tree/master/src/stackoverflow/59829676

Tuddor answered 21/1, 2020 at 3:53 Comment(4)
Thanks for taking your time to write an answer @slideshowp2 . However, using the exact code you wrote I'm still seeing TypeError: Cannot read property 'query' of undefinedMoccasin
In the IDE when hovering the .query method in client.query.mockResolvedValueOnce(mGraphQLResponse) it says: (method) ApolloClient<any>.query<T, TVariables>(options: QueryOptions<TVariables>): Promise<ApolloQueryResult<T>> There is however an error on .mockedResolvedValue(...) instead: Property 'mockResolvedValueOnce' does not exist on type '<T = any, TVariables = OperationVariables>(options: QueryOptions<TVariables>) => Promise<ApolloQueryResult<T>>'.ts(2339)Moccasin
@Moccasin You can ignore the type check of TypeScript and try again.Tuddor
Still no luck. client somehow stays undefined after const client = setupApi()Moccasin
G
1

Use blow mock class:

class ApolloClient {
  constructor(uri: string, fetch: any, request: any) {}
  setupApi() {
    return {
      query: jest.fn(),
    };
  }
  query() {
    return jest.fn();
  }
}
module.exports = ApolloClient;

and add below line to jest.cofig.ts

 moduleNameMapper: {
'apollo-boost': '<rootDir>/.jest/appolo-client.ts',

},

Gradeigh answered 9/6, 2022 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.