Skip to content

A Solution for Stubbing GraphQL Requests #122

Closed
@davemcorwin

Description

@davemcorwin

Here is a potential solution to stubbing graphql requests in Cypress

Caveats

  • the requests cannot be waited on, but they do return promises that resolve immediately so it shouldn't be an issue in practice
  • a new visit is required to set or change a stub, so this can have a negative performance impact on the tests
  • assumes your app is using fetch but can probably be modified to work for xhr
  • assumes you are using Cypress v0.20.x (I think)
  • does not provide any error handling

Details

1. Create a Cypress Command to make life easier

/*
 * Create a command named `visitStubbed` that wraps the normal `cy.visit`. It
 * takes the url as the first parameter and * an object with your graphql
 * request stubs. Each key of the object must match the `operationName` of the
 * graphql request to be stubbed and each value is an object containing the
 * stubbed graphql response.
 *
 * Ex.
 * ```
 * cy.visitStubbed('/home', {
 *   fetchWidgets: {
 *     data: {
 *       widgets: [{
 *         id: 1,
 *         name: 'Cool Widget',
 *         __typename: 'Widget',
 *         //...
 *       }]
 *    }
 *  }
 * })
 * ```
 */

// import the function from the following step (optional)
import { runQuery } from '../support/graphql'

Cypress.Commands.add('visitStubbed', function(url, operations = {}) {
  cy.visit(url, {
    onBeforeLoad: win => {
      cy
        // stub `fetch`
        .stub(win, 'fetch')
        
        // your graphql endpoint
        .withArgs('/graphql')

        // call our stub
        .callsFake(serverStub)
    },
  })

  function serverStub(_, req) {
    // parse the request
    const { operationName, query, variables } = JSON.parse(req.body)

    // return the stub if it was provided
    const resultStub = operations[operationName]
    if (resultStub) {
      return Promise.resolve(responseStub(resultStub))
    }
    // else {
    //   return {} 
    // }

    // If you want, fallback to default mock data if stub for operation is not specified (optional)
    return runQuery(query, variables).then(responseStub)
  }
})

function responseStub(result) {
  return {
    json() {
      return Promise.resolve(result)
    },
    ok: true,
  }
}

2. Create a mock GraphQL server (Optional)

You only need this if you want a fallback to a mock server. This requires your schema in SDL format to be exported as a string from a .js file. We handle this by generating the file before running Cypress using get-graphql-schema:

ORIGIN=https://some-url.com

SCHEMA_FILE=cypress/schema.js

echo "module.exports = \`" > $SCHEMA_FILE
node_modules/.bin/get-graphql-schema $URL >> $SCHEMA_FILE
echo "\`" >> $SCHEMA_FILE
import { makeExecutableSchema, addMockFunctionsToSchema } from 'graphql-tools'
import { graphql } from 'graphql'
// Your schema in SDL format exported as a string
import typeDefs from '../schema'

const schema = makeExecutableSchema({ typeDefs })
addMockFunctionsToSchema({ schema })

export function runQuery(query, variables) {
  return graphql(schema, query, {}, {}, variables)
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions