Testing GraphQL APIs

◕ GraphQLLesson 14Advanced

Testing GraphQL APIs requires a slightly different approach than REST — since all requests go to a single endpoint, you test by sending GraphQL operation strings. The @apollo/server testing utilities and Jest make this straightforward.

1 Unit Testing Resolvers
JavaScript — Resolver Unit Tests
// resolvers.test.js
const { resolvers } = require("../src/resolvers");

describe("Query.user", () => {
  it("returns a user when found", async () => {
    const mockUser = { id: "1", name: "Balaji", email: "b@test.com" };
    const mockDB   = { User: { findById: jest.fn().mockResolvedValue(mockUser) } };
    const mockCtx  = { currentUser: { id: "1", role: "USER" }, db: mockDB };

    const result = await resolvers.Query.user(null, { id: "1" }, mockCtx);
    expect(result).toEqual(mockUser);
    expect(mockDB.User.findById).toHaveBeenCalledWith("1");
  });

  it("throws UNAUTHENTICATED when no user in context", async () => {
    const ctx = { currentUser: null };
    await expect(
      resolvers.Query.user(null, { id: "1" }, ctx)
    ).rejects.toThrow("Authentication required");
  });
});
2 Integration Tests with Apollo Test Client
JavaScript — Apollo Server Integration Tests
const { ApolloServer } = require("@apollo/server");
const { typeDefs } = require("../src/schema");
const { resolvers } = require("../src/resolvers");

describe("GraphQL Integration Tests", () => {
  let server;

  beforeAll(async () => {
    server = new ApolloServer({ typeDefs, resolvers });
    await server.start();
  });

  afterAll(() => server.stop());

  it("fetches all users", async () => {
    const { body } = await server.executeOperation({
      query: "{ users { id name email } }"
    });

    expect(body.kind).toBe("single");
    expect(body.singleResult.errors).toBeUndefined();
    expect(Array.isArray(body.singleResult.data.users)).toBe(true);
  });

  it("creates a user via mutation", async () => {
    const { body } = await server.executeOperation({
      query:     "mutation CreateUser($input: CreateUserInput!) { createUser(input: $input) { id name } }",
      variables: { input: { name: "Test User", email: "test@test.com", password: "Pass123!" } }
    }, {
      contextValue: { currentUser: null }
    });

    expect(body.singleResult.data.createUser.name).toBe("Test User");
  });

  it("returns UNAUTHENTICATED error for protected queries", async () => {
    const { body } = await server.executeOperation(
      { query: "{ me { id name } }" },
      { contextValue: { currentUser: null } }
    );

    expect(body.singleResult.errors[0].extensions.code).toBe("UNAUTHENTICATED");
  });
});
3 Code Challenge
Challenge: Write a complete test suite for a Post resolver covering: listing posts (unauthenticated), creating a post (authenticated), updating a post (owner only), and deleting a post (admin only). Use mock contexts with different roles.