Skip to content

Testing GraphQL APIs — Queries, Mutations & Schema Testing Guide

DodaTech Updated 2026-06-24 5 min read

In this tutorial, you'll learn about Testing Graphql APIs. We cover key concepts, practical examples, and best practices.

GraphQL API testing differs from REST testing because you have a single endpoint, flexible queries, and strongly typed schemas that define exactly what data clients can request. In this guide, you will learn how to test GraphQL queries and mutations, validate schema structure and resolver behavior, use snapshot testing for complex nested responses, and automate your GraphQL test suite in CI/CD. The DodaBrowser API team runs over 500 GraphQL tests against every schema change, ensuring the search and recommendation queries never break.

Learning Path

flowchart LR
  A[REST API Testing] --> B[GraphQL Basics]
  B --> C[Testing GraphQL APIs
You are here] C --> D[Schema Validation] D --> E[Performance Testing] style C fill:#f90,color:#fff

Testing GraphQL Queries

Test that queries return the expected shape and data:

import requests, json

GRAPHQL_URL = "http://localhost:4000/graphql"

def run_query(query, variables=None):
    payload = {"query": query}
    if variables:
        payload["variables"] = variables
    r = requests.post(GRAPHQL_URL, json=payload)
    return r.json()

def test_get_user():
    query = """
    query GetUser($id: ID!) {
        user(id: $id) {
            id
            name
            email
        }
    }
    """
    result = run_query(query, {"id": "1"})
    assert "errors" not in result, f"Errors: {result.get('errors')}"
    user = result["data"]["user"]
    assert user["id"] == "1"
    assert "name" in user
    assert "email" in user
    print("GetUser query test passed")

test_get_user()

Expected output:

GetUser query test passed

Testing GraphQL Mutations

Mutations change data and should be tested for success and error states:

def test_create_user():
    mutation = """
    mutation CreateUser($input: CreateUserInput!) {
        createUser(input: $input) {
            id
            name
            email
        }
    }
    """
    result = run_query(mutation, {
        "input": {"name": "Alice", "email": "alice@test.com"}
    })
    assert "errors" not in result, f"Errors: {result.get('errors')}"
    user = result["data"]["createUser"]
    assert user["name"] == "Alice"
    assert user["email"] == "alice@test.com"
    print("CreateUser mutation test passed")

def test_create_user_validation():
    mutation = """
    mutation CreateUser($input: CreateUserInput!) {
        createUser(input: $input) { id name email }
    }
    """
    result = run_query(mutation, {
        "input": {"name": "", "email": "invalid"}
    })
    assert "errors" in result
    errors = [e["message"] for e in result["errors"]]
    assert any("email" in e.lower() for e in errors)
    print("CreateUser validation test passed")

test_create_user()
test_create_user_validation()

Expected output:

CreateUser mutation test passed
CreateUser validation test passed

Schema Validation Testing

Ensure your GraphQL schema defines the correct types, fields, and relationships:

import requests

def get_schema():
    query = """
    query IntrospectionQuery {
        __schema {
            types {
                name
                kind
                fields {
                    name
                    type {
                        name
                        kind
                    }
                }
            }
        }
    }
    """
    result = run_query(query)
    return result["data"]["__schema"]

def test_schema_has_type():
    schema = get_schema()
    type_names = [t["name"] for t in schema["types"]]
    required = ["User", "Query", "Mutation", "CreateUserInput"]
    for name in required:
        assert name in type_names, f"Missing type: {name}"
    print("Schema type check passed")

def test_user_type_fields():
    schema = get_schema()
    user_type = next(t for t in schema["types"] if t["name"] == "User")
    field_names = [f["name"] for f in user_type["fields"]]
    expected = ["id", "name", "email", "createdAt"]
    for field in expected:
        assert field in field_names, f"Missing field: {field}"
    print("User type field check passed")

test_schema_has_type()
test_user_type_fields()

Snapshot Testing GraphQL Responses

import json

def test_user_query_snapshot():
    query = "{ user(id: 1) { id name email createdAt } }"
    result = run_query(query)
    snapshot = {
        "data": {
            "user": {
                "id": "1",
                "name": "Alice",
                "email": "alice@test.com",
                "createdAt": "2026-06-24T00:00:00Z"
            }
        }
    }
    assert result == snapshot, f"Snapshot mismatch: {json.dumps(result, indent=2)}"
    print("Snapshot test passed")

Testing Error Handling

GraphQL returns errors in a structured format — test them explicitly:

def test_not_found():
    query = "{ user(id: 99999) { id name } }"
    result = run_query(query)
    assert "errors" in result
    assert any("not found" in e["message"].lower() for e in result["errors"])
    assert result["data"]["user"] is None
    print("Not found error test passed")

def test_unauthorized():
    query = "{ adminPanel { settings } }"
    result = run_query(query)
    assert "errors" in result
    assert any("unauthorized" in e["message"].lower() or "not authorized" in e["message"].lower()
              for e in result["errors"])
    print("Authorization error test passed")

test_not_found()
test_unauthorized()

Practice Questions

1. How is GraphQL API testing different from REST API testing?

GraphQL has a single endpoint and flexible queries. You test query structure, resolver logic, and schema types rather than fixed URL paths and status codes.

2. What is schema introspection and why is it useful for testing?

Introspection queries return the complete schema definition. You can use it to automatically validate that the schema matches your expected type structure.

3. How do you test GraphQL error handling?

GraphQL returns errors in a structured errors array alongside data. Test for specific error messages, error codes, and null data fields.

4. What is N+1 in GraphQL and how do you test for it?

N+1 occurs when a resolver makes a database query for each item in a list. Test by enabling query logging and checking that list queries produce a single batch query.

Challenge: Build a GraphQL API test suite for an e-commerce schema with types: Product, Order, Customer. Test: product search with filters, order creation with validation, customer profile update, schema introspection for all types, and error handling for out-of-stock products.

FAQ

What tools can I use to test GraphQL APIs?

GraphQL-request, Apollo Client for integration tests, Playwright for end-to-end tests, and GraphQL Inspector for schema validation.

How do I test GraphQL subscriptions?

Subscriptions use WebSockets. Test them with a WebSocket client library like <a href="/apis/graphql/">graphql</a>-ws or use Apollo's execute for in-process subscription testing.

Should I test the schema or the resolvers?

Both. Schema tests verify the API contract. Resolver tests verify business logic. Integration tests verify they work together.

How do I test query complexity limits?

Send deeply nested queries and verify the API rejects them with a complexity error before they reach resolvers.

What's Next

Testing WebSocket Connections
REST API Testing Guide
Contract Testing Guide

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro