Home > database >  mock-apollo-client returnig "Response not successful: Received status code 401"
mock-apollo-client returnig "Response not successful: Received status code 401"

Time:03-27

I'm writing a toy app to learn more about Serverless Framework and AWS AppSync etc. I'm trying to do TDD as much as possible. I'm using mock-apollo-client to mock the ApolloClient, and I've run into a problem. When trying to write a test to make sure the arguments to the query are passed, the test always returns a 401 Unauthorized error. It seems as though the real end point is still being called, because when a valid x-api-key is added to the instantiation of the ApolloClient, the test returns the real value from the AppSync server, and not the mock value I'm expecting. I'm using a mock, not spy, so I'm not expecting the real end point to actually be hit. Furthermore When I do add a valid x-api-key the test fails because the function is never called.

 api › recipes › Given a valid recipe id › Should call query with the id as a param

    expect(jest.fn()).toBeCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

I'm expected the test to fail, because the query currently isn't called with any arguments, but instead it fails because the mock function is never called.

What am I doing wrong?

Code File

import { ApolloClient, gql, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://redacted.appsync-api.redacted.amazonaws.com/graphql',
  headers: {
    'x-api-key': 'key-redacted',
  },
  cache: new InMemoryCache(),
});

export const GET_RECIPE_QUERY = gql`
  query {
    getRecipe (title:"Lemon Cheese Cake") {
      title,
      ingredients{
        name,
        amount,
        unit
      },
      steps
    }
  }
`;

const gqlQuery = (title) => {

  return client
    .query({
      query: GET_RECIPE_QUERY,
      variables : { title }
    });
};

export const getRecipe = async (id) => {
  const result = await gqlQuery(id);

  return result.data.getRecipe;
};

Test file

import { createMockClient } from 'mock-apollo-client';

import { GET_RECIPE_QUERY, getRecipe } from './recipes';

const mockRecipe = {
  title: 'Luke\'s Chocolate Orange',
  ingredients: [
    {
      name: 'Orange',
      amount: 1,
    },
    {
      name: 'Chocolate',
      amount: 250,
      unit: 'grams',
    },
  ],
  steps: [
    'Peel orange',
    'Open chocolate',
    'Eat chocolate',
    'Throw orange away',
  ],
};

const mockClient = createMockClient();
const queryHandler = jest.fn().mockResolvedValue({data: {recipe: mockRecipe}});

mockClient.setRequestHandler(GET_RECIPE_QUERY, queryHandler);

describe('api', () => {
  describe('recipes', () => {
    describe('Given a valid recipe id', () => {
      it('Should call query with the id as a param', async () => {
        const id = 'Luke\'s Chocolate Orange';
        const result = await getRecipe(id);
        
        expect(queryHandler).toBeCalledTimes(1);
        expect(queryHandler).toBeCalledWith(id);
      });
    });
  });
});
Packages Versions
@apollo/client 3.5.10
graphql 16.3.0
@testing-library/jest-dom 5.16.2
@testing-library/react 12.1.4
@testing-library/user-event 13.5.0
jest 27.5.1
mock-apollo-client 1.2.0

CodePudding user response:

mock-apollo-client always use the with ApolloProvider, so that you pass the mock apollo client via React context Provider to descendant components.

However, your code cannot pass the mock apollo client to the component in this way. Your code initiates requests directly from the Apollo Client. We need to intercept these GraphQL requests. There are several ways to do this such as msw. However, I'll continue to use the mock-apollo-client library to demonstrate.

You need to mock ApolloClient class of the @apollo/client module. We need to use Mocking Partials, we don't want to mock other things exported from @apollo/client. Since the mock-apollo-client library already provides createMockClient function to create mocked apollo client, we don't need to mock by ourself.

An working example:

recipes.ts:

import { ApolloClient, gql, InMemoryCache } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://redacted.appsync-api.redacted.amazonaws.com/graphql',
  headers: {
    'x-api-key': 'key-redacted',
  },
  cache: new InMemoryCache(),
});

export const GET_RECIPE_QUERY = gql`
  query {
    getRecipe(title: "Lemon Cheese Cake") {
      title
      ingredients {
        name
        amount
        unit
      }
      steps
    }
  }
`;

const gqlQuery = (title) => {
  return client.query({
    query: GET_RECIPE_QUERY,
    variables: { title },
  });
};

export const getRecipe = async (id) => {
  const result = await gqlQuery(id);
  return result.data.getRecipe;
};

recipes.test.ts:

import { createMockClient } from 'mock-apollo-client';

const mockRecipe = {
  title: "Luke's Chocolate Orange",
  ingredients: [
    { name: 'Orange', amount: 1, unit: 'abc' },
    { name: 'Chocolate', amount: 250, unit: 'grams' },
  ],
  steps: ['Peel orange', 'Open chocolate', 'Eat chocolate', 'Throw orange away'],
};
const mockClient = createMockClient();

describe('api', () => {
  describe('recipes', () => {
    describe('Given a valid recipe id', () => {
      beforeEach(() => {
        jest.resetModules();
      });
      it('Should call query with the id as a param', async () => {
        jest.doMock('@apollo/client', () => {
          return {
            ...jest.requireActual('@apollo/client'),
            ApolloClient: jest.fn(() => mockClient),
          };
        });
        const queryHandler = jest.fn().mockResolvedValue({ data: { getRecipe: mockRecipe } });

        const { GET_RECIPE_QUERY, getRecipe } = require('./recipes');
        mockClient.setRequestHandler(GET_RECIPE_QUERY, queryHandler);

        const title = "Luke's Chocolate Orange";
        const result = await getRecipe(title);
        expect(result).toEqual(mockRecipe);
        expect(queryHandler).toBeCalledWith({ title });
      });
    });
  });
});

Test result:

 PASS  src/stackoverflow/71612556/recipes.test.ts
  api
    recipes
      Given a valid recipe id
        ✓ Should call query with the id as a param (91 ms)

------------------------|---------|----------|---------|---------|-------------------
File                    | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
------------------------|---------|----------|---------|---------|-------------------
All files               |   90.91 |      100 |   66.67 |   90.91 |                   
 mocks                  |      75 |      100 |       0 |      75 |                   
  handlers.js           |   66.67 |      100 |       0 |   66.67 | 14                
  server.js             |     100 |      100 |     100 |     100 |                   
 stackoverflow/71612556 |     100 |      100 |     100 |     100 |                   
  recipes.ts            |     100 |      100 |     100 |     100 |                   
------------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        2.775 s

You can find the source code here

  • Related