|

GraphQL Testing for QA Engineers: Queries, Mutations, Schema Validation, and Security

GraphQL APIs are different from REST. Single endpoint, dynamic queries, nested resolvers, N+1 problems. Traditional API testing tools struggle. Here is how to test GraphQL properly.

🎭 Want to master this with real projects? Join the Playwright Automation Mastery course at The Testing Academy.

Contents

GraphQL vs REST Testing Differences

AspectRESTGraphQL
EndpointsMany (/users, /orders)One (/graphql)
Response shapeFixed by serverDefined by client query
Status codes404, 401, 500 etc.Usually 200 (errors in body)
Over-fetchingCommonClient controls fields
VersioningURL-based (/v2/)Schema evolution

Testing GraphQL Queries

test('fetch user with specific fields', async ({ request }) => {
  const response = await request.post('/graphql', {
    data: {
      query: `
        query GetUser($id: ID!) {
          user(id: $id) {
            name
            email
            orders { id total }
          }
        }
      `,
      variables: { id: "1" }
    }
  });

  const { data, errors } = await response.json();
  expect(errors).toBeUndefined();
  expect(data.user.name).toBeDefined();
  expect(data.user.email).toContain('@');
  expect(data.user.orders).toBeInstanceOf(Array);
});

Testing Mutations

test('create user mutation', async ({ request }) => {
  const response = await request.post('/graphql', {
    data: {
      query: `
        mutation CreateUser($input: CreateUserInput!) {
          createUser(input: $input) {
            id
            name
            email
          }
        }
      `,
      variables: {
        input: { name: "Test User", email: "test@example.com", password: "Pass123!" }
      }
    }
  });

  const { data, errors } = await response.json();
  expect(errors).toBeUndefined();
  expect(data.createUser.id).toBeDefined();
  expect(data.createUser.name).toBe("Test User");
});

🚀 Level Up Your Playwright

From locators to CI pipelines — build a production-grade Playwright + TypeScript framework step by step.

GraphQL-Specific Test Cases

  • Query depth limiting: Deeply nested queries should be rejected (prevents DoS)
  • Field authorization: User role should not see admin-only fields
  • N+1 detection: Monitor resolver call count per query
  • Introspection control: Introspection should be disabled in production
  • Error masking: Internal errors should not leak stack traces
  • Batch query limits: Prevent query batching abuse

Schema Validation

test('schema has not changed unexpectedly', async ({ request }) => {
  const response = await request.post('/graphql', {
    data: { query: '{ __schema { types { name fields { name type { name } } } } }' }
  });
  const schema = await response.json();
  // Compare against saved schema snapshot
  expect(schema).toMatchSnapshot('graphql-schema');
});

Security Testing

test('reject deeply nested query (DoS prevention)', async ({ request }) => {
  const deepQuery = '{ user(id:"1") { orders { items { product { reviews { author { orders { items { product { name } } } } } } } } } }';
  const response = await request.post('/graphql', {
    data: { query: deepQuery }
  });
  const { errors } = await response.json();
  expect(errors).toBeDefined();
  expect(errors[0].message).toContain('depth');
});

test('introspection disabled in production', async ({ request }) => {
  const response = await request.post('/graphql', {
    data: { query: '{ __schema { types { name } } }' }
  });
  const { errors } = await response.json();
  expect(errors).toBeDefined();
});

🎓 Master Playwright End to End

Join hundreds of SDETs building real automation frameworks. Lifetime access, hands-on projects, and a job-ready portfolio.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.