Apollo-client: Can't test loading state using MockedProvider

Created on 4 Jun 2020  路  13Comments  路  Source: apollographql/apollo-client

Intended outcome:
I tried to test loading state

import {act, cleanup, render} from 'react-native-testing-library';
import { MockedProvider } from '@apollo/client/testing';

const {getByTestId} = render(
    <MockedProvider mocks={[]} addTypename={false}>
      {<ProductsScreen {...props} />}
    );
    await act(() => wait(0));
    expect(getByTestId('ActivityIndicator')).not.toBe(null);

Actual outcome:
but it gives me an error
No more mocked responses for the query

Versions
System:
OS: macOS 10.15.3
Binaries:
Node: 10.18.1
Yarn: 1.21.1
npm: 6.13.6
Browsers:
Chrome: 83.0.4103.97
Safari: 13.0.5
npmPackages:
@apollo/client: ^3.0.0-rc.0 => 3.0.0-rc.0

Most helpful comment

Same here, hundreds of our tests failed because of this issue. Please bring back the old behavior.

All 13 comments

you need to pass it the query, variables and expected result

I am attempting to upgrade to @apollo/client with today's new release and having the same issue with tests for loading states.

+1

I have same issue on @apollo/client 3.0.1 version

 it("should render loading spinner", () => {
      const { getByTestId } = render(
        <ApolloMockedProvider mocks={[]} addTypename={false}>
          <SomeComponent />
        </ApolloMockedProvider>,
      );

      expect(getByTestId("Spinner")).toBeVisible();
    });

this test code is failed. and error message

   Error: Uncaught [Error: No more mocked responses for the query: query xxxx

I saw the code related to this issue.

when I tested it by attaching @apolo/client to a project newly created with CRA, no issue occurred.

To be exact, I had to go through the error because I didn't have the mock query, but the test didn't fail.

I think Among the parts dealing with Observable, there seems to be a case with and without an error handling.

Same problem here... Trying to update to @apollo/client and got tens of these "No more mocked responses" errors. Also in the simple loading state check.

https://github.com/apollographql/apollo-client/blob/master/src/utilities/testing/mocking/mockLink.ts#L89

There are times when the test fails and there are times when the error is thrown an error in this part.

FWIW,
In my opinion, when MockedProvider receives mocks={[]}, it should detect this as the intentional "loading" input, and just return null without throwing an error. That will keep the provider in the loading state indefinitely, and will save users the current headaches:

  • unwanted errors when intentionally providing no mocked response, which are causing some people failing tests
  • or tests still pass, but with a clutter of unwanted false-positive log messages (this is what is bothering me)

I believe the way to go is to add something like this at line 76 of mockLink.ts (just a guess. I'm not sufficiently knowledgeable about the codebase):

if (!key) return null; // stay in the loading state without logging an error

This creates separation between cases where we want the error (a requested key does not exist in the mock) and when we do not want the error (no keys exist in the mock).

I have a workaround that appears to be working for me at the moment on @apollo/client v3.0.2.

Using the OPs code as a starting point (NOT WORKING):

import {act, cleanup, render} from 'react-native-testing-library';
import { MockedProvider } from '@apollo/client/testing';

const {getByTestId} = render(
    <MockedProvider mocks={[]} addTypename={false}>
      {<ProductsScreen {...props} />}
    );
    await act(() => wait(0));
    expect(getByTestId('ActivityIndicator')).not.toBe(null);

I recommend three changes:

  • use a real mock object that includes a response. This will prevent the No more mocked responses error.
  • expect before wait. We want to run our expectation against the initial state, which is the loading state.
  • act-wait after expect. MockedProvider will update state after our expectation. To keep our test clean of React act() warnings, we need to wrap that state change in act.

All together, that looks like this (WORKAROUND SOLUTION):

import {act, cleanup, render} from 'react-native-testing-library';
import { MockedProvider } from '@apollo/client/testing';
import {myMock} from "./myMocks";

const {getByTestId} = render(
    <MockedProvider mocks={[myMock]} addTypename={false}>
      {<ProductsScreen {...props} />}
    );
    expect(getByTestId('ActivityIndicator')).not.toBe(null);
    await act(() => wait(0));

So those shenanigans get my Apollo-loading tests passing stably in any order again. 馃檶
But it would be much nicer if a mock of [] would not throw a No more mocked responses error to begin with. Then there would be no state change to worry about wrapping in act, and no need to sneak in an expectation before the next tick, and testing the loading state would be nice and straightforward again. 馃榿

Hitting this while upgrading my project, it would be nice if there was a MockProvider mode that operated in a less strict mode, as it did prior to the update.

Same here, hundreds of our tests failed because of this issue. Please bring back the old behavior.

This behavior is even suggested in the documentation, though of course also giving the same Error:
https://www.apollographql.com/docs/react/development-testing/testing/#testing-loading-states

it('should render loading state initially', () => {
  const component = renderer.create(
    <MockedProvider mocks={[]}>
      <Dog />
    </MockedProvider>,
  );

  const tree = component.toJSON();
  expect(tree.children).toContain('Loading...');
});

+1 I too have followed the documentation at https://www.apollographql.com/docs/react/development-testing/testing/#testing-loading-states. I am using react testing library. The same error about not more requests/responses is raised. This is really annoying...

For anyone still experiencing this bug (I am, even with everything up to date) I came up with an interim fix that doesn't patch anything.

Add a new LoadingApolloLink.js somewhere in your project:

import { __extends } from 'tslib';
import { MockLink } from '@apollo/client/utilities/testing/mocking/mockLink';

var LoadingApolloLink = (function (_super) {
  __extends(LoadingLink, _super);
  function LoadingLink() {
    var _this = _super.call(this) || this;
    return _this;
  }
  LoadingLink.prototype.request = function (operation) {
    return false;
  };
  return LoadingLink;
})(MockLink);

export { LoadingApolloLink };

Now use this link in your provider:
Note: I haven't actually tried this out in tests, only Storybook

<MockedProvider link={LoadingApolloLink}>

My story parameter setup looks like this:

  apolloClient: {
    link: new LoadingApolloLink()
  }
Was this page helpful?
0 / 5 - 0 ratings