React-apollo: MockedProvider can't mock queries with client directives

Created on 6 Aug 2019  路  8Comments  路  Source: apollographql/react-apollo

Intended outcome:

When using a MockedProvider and a query with client directives, the client directives should resolve.

Query example:

query Cats {
  cats {
    name @client
    breed
  }
}

Actual outcome:

Error:
Network error: No more mocked responses for the query

How to reproduce the issue:

const CATS = gql`
  query Cats {
    cats {
      name @client
      breed
    }
  }
`;

const Container: React.FC<{}> = () => (
  <Query query={CATS}>
    {({ data, loading, error }) => {
      if (error) return <pre>{JSON.stringify(error)}</pre>;
      if (loading) return <pre>Loading</pre>;
      if (data) return <pre>{JSON.stringify(data)}</pre>;
    }}
  </Query>
);

const waitForUpdate = async wrapper => {
  await new Promise(resolve => setTimeout(resolve));
  wrapper.update();
};

const data = {
  cats: { name: 'Fluffy', breed: 'kitten' }
}

it('loads', async () => {
  const wrapper = mount(
    <MockedProvider
      mocks={[
        {
          request: {
            query: CATS
          },
          result: {
            data,
          }
        }
      ]}
    >
      <Container />
    </MockedProvider>
  );

  await waitForUpdate(wrapper);
  expect(wrapper).toHaveLength(1);
  expect(wrapper.find('pre').text()).toEqual(
    JSON.stringify(data)
  );
});

This test fails with:

    Expected value to equal:
      "{\"cats\":{\"name\":\"Fluffy\",\"breed\":\"kitten\"}}"
    Received:
      "{\"graphQLErrors\":[],\"networkError\":{},\"message\":\"Network error: No more mocked responses for the query: query Cats {\\n  cats {\\n    name @client\\n    breed\\n    __typename\\n  }\\n}\\n, variables: {}\"}"

EDIT:
The test passes if I remove @client from the query.

Version

System:
OS: Linux 5.2 Arch Linux undefined
Binaries:
Node: 11.4.0 - ~/.nvm/versions/node/v11.4.0/bin/node
Yarn: 1.17.3 - /usr/bin/yarn
npm: 6.4.1 - ~/.nvm/versions/node/v11.4.0/bin/npm
Browsers:
Firefox: 68.0.1
npmPackages:
apollo-boost: ^0.4.3 => 0.4.3
apollo-client: ^2.6.3 => 2.6.3
react-apollo: ^2.5.8 => 2.5.8

waiting-response

Most helpful comment

@elaich MockedProvider creates its own ApolloClient instance internally, that by default doesn't know anything about your local resolvers. To work around this, you can pass your resolvers into the MockedProvider resolvers prop like <MockedProvider resolvers={...your resolver object...}>. Does that help?

All 8 comments

@elaich MockedProvider creates its own ApolloClient instance internally, that by default doesn't know anything about your local resolvers. To work around this, you can pass your resolvers into the MockedProvider resolvers prop like <MockedProvider resolvers={...your resolver object...}>. Does that help?

@hwillson It did help, I had to change my waitForUpdate function to wait a litte bit more and add addTypename={false}

const waitForUpdate = async wrapper => {
  await new Promise(resolve => setTimeout(resolve, 10));
  wrapper.update();
};

const resolvers = {
  Cat: {
    name: () => {
      return 'Name'
    }
  }
}

it('loads', async () => {
  const wrapper = mount(
    <MockedProvider
      resolvers={resolvers}
      mocks={[
        {
          request: {
            query: CATS
          },
          result: {
            data,
          }
        }
      ]}
      addTypename={false}
    >
      <Container />
    </MockedProvider>
  );
});

@hwillson any chance of throwing a specific error in this case? I just spent the whole day trying to figure out why the canned responses didn't work when swapping out apollo-link-mock (which is just taken from this repo before #2776 was merged) with @apollo/react-testing. In our case we construct a client with a MockLink manually, but the issue was missing local resolvers (same as OP). Getting a No more mocked responses for the query wasn't really helpful - the query is huge. Maybe detect a @client annotation within the query as well?

@SimenB I definitely agree - I've been hit by this many times as well. Would you mind creating an FR for this in the https://github.com/apollographql/apollo-feature-requests repo?

I've tried implementing everything mentioned in this post as above to no avail
https://github.com/shadrech/state-management-react-apollo/blob/tests/app/src/components/WorkerList/index.test.jsx
So my test file looks like

import React from "react";
import { mount } from "enzyme";
import wait from "waait";
import { MemoryRouter } from "react-router";
import { MockedProvider } from "@apollo/react-testing";
import WorkerList from ".";
import workersQuery from "../../graphql/queries/workers.graphql";
import resolvers from "../../graphql/resolvers";
import { InMemoryCache } from 'apollo-boost';

const workers = [{
  id: 1,
  firstName: 'Tatenda',
  lastName: 'Chawanzwa',
  email: '[email protected]',
  isOpen: false,
  bio: 'My short life story blah blah blah',
  skills: ['Node', 'React', 'Basketball', 'Speaker'],
  __typename: 'Worker'
}, {
  id: 2,
  firstName: 'Jon',
  lastName: 'Brian',
  email: '[email protected]',
  isOpen: false,
  bio: 'The Jon bio',
  skills: ['Not sure', 'Teacher'],
  __typename: 'Worker'
}];

const defaultMocks = [{
  request: {
    query: workersQuery,
    variables: {
      options: { limit: 200 },
      conditions: {}
    }
  },
  result: {
    data: { workers }
  }
}];

async function renderComponent(component, mocks = defaultMocks) {
  const cache = new InMemoryCache();
  const app = mount(
    <MockedProvider mock={mocks} addTypename={false} cache={cache} resolvers={resolvers}>
      <MemoryRouter>
        {component}
      </MemoryRouter>
    </MockedProvider>
  );
  await wait(10);
  app.update();
  return app;
}

describe('WorkerList', () => {

  test('should render as expected', async () => {
    const component = await renderComponent(<WorkerList />);
    console.log(component.debug());
  });

});

Actual component in question:

import React from "react";
import { useQuery } from "@apollo/react-hooks";
import Worker from "../Worker";
import { StyledLink, WorkersWrapper } from "./styles";
import { Wrapper } from "../App/styles";
import workersQuery from "../../graphql/queries/workers.graphql";

export default () => {
  const { loading, error, data } = useQuery(workersQuery, {
    variables: {
      options: { limit: 200 },
      conditions: {}
    }
  });
  return (
    <Wrapper>
      <StyledLink to="/worker/create">
        <i className="fas fa-user-plus"></i>
      </StyledLink>
      <WorkersWrapper>
        {!loading && !error && data.workers.map(worker => <Worker key={worker.id} worker={worker} />)}
      </WorkersWrapper>
    </Wrapper>
  );
}

Not sure what I'm doing wrong to get the No more mocked responses for the query... error. Any ideas @hwillson

@shadrech try removing resolvers and addTypename: false, and add:

cache.writeData({ data: { workers } })

You didn't include your query so I'm guessing a bit about your store structure. Also add typeNames.

I could mock the @client queries using resolvers via MockedProvider.
To mock non client queries and mutations , use mocks in MockProvider:

const savePet = jest.fn();
  const dataMocks = [
    {
      request: {
        query: GET_PETS,
        variables: {
          petDto: {
            categoryId: 'DOG',
            id: '355'
          }
        }
      },
      result: {
        data: {
          pets: {
            id: '355',
            name: 'BRUNO',
            category: 'DOG',
            __typename: 'Pets'
          }
        }
      }
    },
    {
      request: {
        query: SAVE_PETS,
        variables: {
          petDto: {
            id: '355',
            name: 'BRUNO',
            category: 'DOG',
            __typename: 'Pets'
          }
        }
      },
      result: () => {savePet(); return {data: {savePet: true}}
      }
    },
  ];
  const resolvers = {
    Query: {
      getOwner: () => {
        return {
          ownerId: '2001',
          ownerName: 'Mark',
          ownerAddress: '321 Street',
          __typename: 'Owner'
        };
      }

    }
  };

  const renderWithMockProvider = () =>
    render(
      <MockedProvider mocks={dataMocks} resolvers={resolvers}>
          <PetsDetails />
      </MockedProvider>
    );
Was this page helpful?
0 / 5 - 0 ratings