DEV Community

loading...

E2E Testing with Cypress and GraphQL

Warren Day
Originally published at hashnode.com ・3 min read

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');
});
Enter fullscreen mode Exit fullscreen mode

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

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

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
}
Enter fullscreen mode Exit fullscreen mode

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'
    })
  }),
});
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Screenshot 2021-07-09 at 18.02.23.png

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

Discussion (2)

Collapse
cubo25 profile image
Jakub Vitek

I am looking for an easy way to stub graphQL any ideas or similar solution as this you provided ?

Thanks a lot !

Collapse
warrenday profile image
Warren Day Author

Hey Jakub.

What is your use-case and is there something in particular you are trying to do that this library can not help with?