You can test your GraphQL resolvers and schemas with Jest by treating them as plain JavaScript functions and objects, which Jest is excellent at asserting against.

Let’s see it in action. Imagine you have a simple GraphQL schema and a resolver for fetching a user by ID.

# schema.graphql
type User {
  id: ID!
  name: String!
}

type Query {
  user(id: ID!): User
}
// resolvers.js
const users = {
  '1': { id: '1', name: 'Alice' },
  '2': { id: '2', name: 'Bob' },
};

const resolvers = {
  Query: {
    user: (parent, { id }, context, info) => {
      // In a real app, this would fetch from a database
      return users[id];
    },
  },
};

export default resolvers;

Now, let’s write a Jest test for this. We’ll need a GraphQL schema definition and a way to execute queries against our resolvers. The graphql package from graphql-js is perfect for this.

// resolvers.test.js
import { graphql, buildSchema } from 'graphql';
import resolvers from './resolvers'; // Assuming resolvers.js is in the same directory

describe('GraphQL Resolvers', () => {
  let schema;

  // Build the schema once for all tests in this describe block
  beforeAll(() => {
    const typeDefs = `
      type User {
        id: ID!
        name: String!
      }

      type Query {
        user(id: ID!): User
      }
    `;
    schema = buildSchema(typeDefs);
  });

  it('should correctly resolve a user by ID', async () => {
    const query = `
      query GetUser($userId: ID!) {
        user(id: $userId) {
          id
          name
        }
      }
    `;
    const variables = { userId: '1' };

    // Execute the query against our schema and resolvers
    const response = await graphql({
      schema,
      source: query,
      rootValue: resolvers, // Pass our resolvers as rootValue
      variableValues: variables,
    });

    // Assertions
    expect(response.errors).toBeUndefined(); // Ensure no GraphQL errors occurred
    expect(response.data).toBeDefined();
    expect(response.data.user).toEqual({
      id: '1',
      name: 'Alice',
    });
  });

  it('should return null if user ID is not found', async () => {
    const query = `
      query GetUser($userId: ID!) {
        user(id: $userId) {
          id
          name
        }
      }
    `;
    const variables = { userId: '99' }; // Non-existent ID

    const response = await graphql({
      schema,
      source: query,
      rootValue: resolvers,
      variableValues: variables,
    });

    expect(response.errors).toBeUndefined();
    expect(response.data).toBeDefined();
    expect(response.data.user).toBeNull(); // Expecting null for a not-found user
  });
});

This setup allows you to treat your GraphQL resolvers as regular functions that take arguments (parent, args, context, info) and return data. Jest’s assertion library (expect) then lets you verify the shape and content of the returned data. You’re essentially unit testing the logic within your resolvers.

The graphql function from graphql-js is the core of this testing strategy. It takes your schema, the source (your GraphQL query string), rootValue (where your resolvers live), and variableValues (for parameterized queries). It then executes the query and returns a promise with the data and any errors.

The mental model here is that GraphQL is just a query language for a graph of data, and your resolvers are the functions that traverse that graph. By providing your resolvers directly to the graphql execution function, you bypass the network layer and any HTTP server specifics, allowing for fast, focused unit tests. You can also mock the context argument to simulate authentication or other contextual data that your resolvers might depend on.

When testing, you’re not just checking if a resolver returns the correct data, but also if it handles edge cases gracefully. For instance, the second test checks what happens when a requested id doesn’t exist in our mock users object. A robust resolver should return null for that field, which is what we assert.

Most people don’t realize how simple it is to mock the context argument. If your user resolver, for example, needed to check if the current user was an administrator before returning sensitive data, you could pass a mocked context object to graphql:

const response = await graphql({
  schema,
  source: query,
  rootValue: resolvers,
  variableValues: variables,
  contextValue: {
    currentUser: { id: '1', isAdmin: true } // Mock context
  }
});

Then, inside your resolver, you’d access context.currentUser.isAdmin. This allows you to test authorization logic and other contextual dependencies without spinning up a full server.

The next step is usually integrating this into a CI/CD pipeline, ensuring your GraphQL API remains stable as you develop.

Want structured learning?

Take the full Graphql-tools course →