user3009902
user3009902

Reputation: 97

Testing Apollo Mutation graphql errors

According to the docs, I should be able to to mock graphql errors in order to test.

To simulate GraphQL errors, simply define errors along with any data in your result.

const dogMock = {
  // ...
  result: {
    errors: [{ message: "Error!" }],
  },
};

However the error is causing my test to fail outright. It's working as expected for a Query but the docs say it works the same for a Mutation as well.

For the sake of simplicity, the error case for mutations hasn’t been shown here, but testing Mutation errors is exactly the same as testing Query errors: just add an error to the mock, fire the mutation, and check the UI for error messages.

Here is my code...

Component

class Example extends Component {
  state = {email: ''};

  render() {
    return (
      <div>
        <Mutation mutation={EXAMPLE_MUTATION} variables={this.state}>
          {(signin, {error, loading}) => {
            if(error)
              return <p data-test="graphql-error">{error.message}</p>;

            return (
              <form method="post" onSubmit={async e => {
                e.preventDefault();
                await signin();
                this.setState({email: ''});
              }}>
                <fieldset disabled={loading} aria-busy={loading}>
                  <label htmlFor="email">
                    Email
                    <input type="email" name="email" placeholder="email" value={this.state.email} onChange={this.saveToState} />
                  </label>
                  <div>
                    <input type="submit" value="Example"/>
                  </div>
                </fieldset>
              </form>
            )
          }}
        </Mutation>
      </div>
    );
  }

  saveToState = e => this.setState({[e.target.name]: e.target.value});
}

Mutation

const EXAMPLE_MUTATION = gql`
  mutation EXAMPLE_MUTATION($email: String!){
    example(email: $email){
      email
      name
    }
  }
`;

Test


describe('<Example />', () => {
  it('handles errors properly', async () => {
    const wrapper = mount(
      <MockedProvider mocks={[{
        request: {query: EXAMPLE_MUTATION, variables: {email: '[email protected]'}},
        result: {errors: [{message: "There was an error"}]}}]}>
        <Example />
      </MockedProvider>
    );

    type(wrapper, 'email', '[email protected]');
    wrapper.update();
    wrapper.find(`form`).simulate('submit');

    // form is busy + contains inputs
    expect(toJSON(wrapper.find('form'))).toMatchSnapshot();

    await wait();
    wrapper.update();

    // test fails before ever getting here

    const err = wrapper.find('[data-test="graphql-error"]');
    expect(err.text()).toEqual('There was an error');

    console.log(err.debug())
    console.log(err.text())
  });

The test fails with my error instead of rendering it to the page like I would have expected.

    GraphQL error: There was an error

      at new ApolloError (node_modules/src/errors/ApolloError.ts:56:5)
      at Object.next (node_modules/src/core/QueryManager.ts:258:21)
      at notifySubscription (node_modules/zen-observable/lib/Observable.js:130:18)
      at onNotify (node_modules/zen-observable/lib/Observable.js:165:3)
      at SubscriptionObserver.next (node_modules/zen-observable/lib/Observable.js:219:7)
      at node_modules/react-apollo/test-utils.js:937:38
      at Timeout.callback (node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)

Where did I go wrong? Any help would be much appreciated!

Upvotes: 8

Views: 7458

Answers (2)

Antajo Paulson
Antajo Paulson

Reputation: 584

Create ApolloError object and pass it as error field in the mock variable

   const apolloError = new ApolloError({
      graphQLErrors: [new GraphQLError('SLOT_ALREADY_BOOKED')],
      networkError: null,
    });
    
    <MockedProvider
      mocks={[
        {
          error: apolloError,
          request: { query: EXAMPLE_MUTATION, variables: { email: '[email protected]' } },
        },
      ]}
    >
      <Example />
    </MockedProvider>;

Upvotes: 3

Sergey Lapin
Sergey Lapin

Reputation: 2693

When mutation fails, react-apollo throws an error if no onError prop is provided to Mutation component (source code here). If you leave the error uncaught, test runner interprets it as a failed test.

This can be worked around by either preventing the error from throwing by providing a dummy onError prop:

<Mutation mutation={EXAMPLE_MUTATION} variables={this.state} onError={() => {}}>

or by wrapping mutation call in a try...catch (await is important since the error is asynchronous):

try {
  await signin();
} catch (error) {
  // do smth with the error here
}

There's a relevant GitHub issue.

Upvotes: 0

Related Questions