E2E Testing with Cypress and GraphQL

You have a full-stack application and your server is running on GraphQL. It's common practice to mock out your back-end so you can test your front-end in isolation. They are separate systems after all and should be tested separately.

Until now this has been quite a difficult task. By default, cypress has poor support for mocking GraphQL servers.

This is a guide to achieving seamless GraphQL mocking in your cypress tests. With this you can easily test happy paths, edge cases and error states; all from the comfort of a single test file.

We'll be using a library specifically built for this problem.

https://github.com/warrenday/cypress-graphql-mock-network

With cypress-graphql-mock-network, you can provide your own GraphQL schema for auto-mocking, which means you only need to mock the parts you care about for each test.

Here's an example of how a test would look:

it('displays initial list of todos', () => {
  cy.mockNetworkAdd({
    Query: () => ({
      todos: () => ([
        {
          id: '1',
          title: 'Go shopping',
          completed: true,
        },
      ]),
    }),
  });

  cy.get('li')
    .eq(0)
    .contains(/Go shopping/)
    .should('exist');
});

Here's an example of how we might mock an error state

cy.mockNetworkAdd({
  Query: () => ({
    todos: () => {
      throw new Error('Oh dear');
    },
  }),
});

Automocking

Under the hood we're using the mocking support of graphql-tools. So you only need to supply the parts of the mock you care about for a test. The rest will be automatically filled in based on the field's type.

Let's say we have the following schema

type Todo {
  id: ID
  title: String
  completed: Boolean
}

type Query {
  todo(id: ID!): Todo
}

In our mocks, if all we cared about was the title, we could do the following:

cy.mockNetworkAdd({
  Query: () => ({
    todo: () => ({
      title: 'I expect to be this'
    })
  }),
});

Even if our application were to query for id, title and completed the mock would still work. We would end up receiving something like:

{
  "id": 1,
  "title": "I expect to be this",
  "completed": false
}

Here id and completed are auto-mocked based on their type, so you can keep your tests streamlined and avoid providing a bunch of data you don't care about.

Service Workers

They'll be no monkey-patching here lad. cypress-graphql-mock-network uses the awesome https://github.com/mswjs/msw meaning real network requests are sent from your app and all mocking is inspectable in the network tab and console. This helps a ton when debugging.

The browser will continue to use the real Fetch and XHR APIs, which is much more realistic to a production environment.

Here you can see the network tab shows the request and the mocked response.
mock-network-example.png

Setup

A full setup guide is available on GitHub, which also includes a demo cypress project, so head for more details on installation and setup:
https://github.com/warrenday/cypress-graphql-mock-network

To see the demo tests in action, pull the repo then we need to do two things:

  1. Run the demo app: Change directory to /demo, install node_modules with yarn, then run yarn start

  2. Run the cypress tests: At the project root install node_modules again with yarn and then run yarn cypress

With this we should then see our the tests passing.

Thanks for reading. If you have any further questions please let me know.

20