Background

Why would you need to mock Cypress GraphQL responses? It’s important to have control of the data you are handling. Say you are fetching data from an API that is replying with thousands of objects, you don’t need all of that data. So mocking the response data we are able to easily make assertions and know exactly what to expect. Mocking the response data with fixtures is also a number times faster than using data from an API since we are making a one-way trip rather than a round trip.

The initial challenge you will face is figuring out the necessary queries and mutations. With REST API’s there are multiple URL paths for obtaining specific data. With GraphQL, you have only one URL to gather a variety of data. When stubbing data, you want to add the fixtures to the before-hook or right before the exception/action that triggers data to be loaded or changed.

A little background on GraphQL queries and mutation. A query is used to read and or fetch data from an API. In its simplest form, a mutation is an operation to write and post values to an API. To learn more on queries/mutations here is a link.

NOTE: This article assumes you have basic knowledge of Cypress/GraphQL and how to make requests with mock data.

Useful utilities to alias queries and mutations from the Cypress docs that will be in use in the examples.

import { CyHttpMessages } from 'cypress/types/net-stubbing';

// Utility to match GraphQL mutation based on the operation name
export const hasOperationName = (
  req: CyHttpMessages.IncomingHttpRequest,
  operationName: string
) => {
  return req.body?.operationName && req.body?.operationName === operationName;
};

// Alias query if operationName matches
export const aliasQuery = (
  req: CyHttpMessages.IncomingHttpRequest,
  operationName: string
) => {
  if (hasOperationName(req, operationName)) {
    req.alias = operationName;
  }
};

// Alias mutation if operationName matches
export const aliasMutation = (
  req: CyHttpMessages.IncomingHttpRequest,
  operationName: string
) => {
  if (hasOperationName(req, operationName)) {
    req.alias = operationName;
  }
};

This article will cover three steps to mocking out Cypress GraphQL responses with mock data/fixtures that will work with any GraphQL request.

  1. The first step is to Alias queries within the Intercept API provided to us by Cypress.
  2. We can then intercept those queries and add conditional data to the specific query or all queries in the same interception.
  3. Finally, we can utilize the routeHandler, which allows us to use its methods to modify the request.

Step 1- Alias Queries and Mutations you want to listen to in the before hook

We want to alias queries and mutations so that Cypress knows what to expect. You can find a list of the aliased and unaliased interceptions at the top left info bar in the Cypress test runner.

FYI, you’ll want to use the “visit” API after the intercept API. Not adding the “visit” will make it so that the test doesn’t navigate anywhere so it’s important to remember that.

describe('Mocking Response data with Cypress GraphQL Apollo', () => {
  before(() => {
    cy.intercept('POST', '*/graphql/v2', (req) => {
      aliasMutation(req, 'UpdateUserMuation');
    });
    cy.visit('/');
  });
});

Step 2 – Intercept queries and mutations

Now that we have aliased queries/mutations, you can add conditional logic to listen to specific queries and mutations in the same interception stated in the before-hook utilizing the utility function hasOperationName.

cy.intercept('POST', '*/graphql/v2', (req) => {
  aliasMutation(req, 'UpdateUserMuation');
  if (hasOperationName(req, 'UpdateUserMuation')) {
  }
});

Step 3 – Utilize the intercept’s routeHandler

Now that you’ve conditionally isolated the query/mutation, you can utilize the intercept’s response parameter. The routeHandler function is typed to the IncomingHttpRequest interface which has multiple methods which are available to use. The method we want to use is “reply”. A reply will allow us to mock out the response data. We can either hardcode the data or use a fixture.

“By specifying a routeHandler function as the last argument to cy.intercept, you’ll have access to the entire request-response session, enabling you to modify the outgoing request, manipulate the real response, make assertions, etc.

The routeHandler takes the incoming HTTP request (IncomingHTTPRequest) as the first argument.” – From the Cypress docs

We can learn more about the routeHandler in the Cypress documentation.

cy.intercept('POST', '*/graphql/v2', (req) => {
  aliasMutation(req, 'UpdateUserMuation')

  if (hasOperationName(req, 'UpdateUserMuation')) {
    req.alias = 'updateUserResponse'

    req.reply((res) => {
      // Modify the response body
      res.body.data.item.name = 'John'
      res.body.data.item.lastName = 'Doe'
    })

    //Alternatively
    req.reply({
      fixture: 'path-to-fixture.json';
    })
  }
})

In conclusion, we were able to go through the steps of breaking down the Intercept API to separate concerns of data and modify response data to a GraphQL API. We added an intercept, then added aliases so we could conditionally set data to specific queries. With the aliased data, we were able to access the routeHandler to modify the API’s reply. Now we have all the steps and knowledge to intercept any GraphQL response.