E2E Testing with Cypress and GraphQL
Tags: javascript, testing, graphql
Published: 2021-07-12 16:41:11

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:

```ts
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

```ts
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

```graphql
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:

```ts
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:

```json
{
  "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](https://cdn.hashnode.com/res/hashnode/image/upload/v1623946762171/sxA6Y2dNR.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.

![Screenshot 2021-07-09 at 18.02.23.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625850150871/_Ov_nb4U7.png)

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