Testing #
The GraphQL HTTP server provides excellent testing capabilities through its built-in test client and integration with testing frameworks.
Built-in Test Client #
The server includes a built-in test client based on Starlette’s TestClient:
from graphql_http import GraphQLHTTP
server = GraphQLHTTP(schema=schema)
client = server.client()
# Test a GraphQL query
response = client.post("/graphql", json={"query": "{ hello }"})
assert response.status_code == 200
assert response.json() == {"data": {"hello": "Hello, World!"}}Basic Testing Example #
Here’s a complete example using pytest:
import pytest
from graphql import (
GraphQLSchema,
GraphQLObjectType,
GraphQLField,
GraphQLString,
GraphQLArgument
)
from graphql_http import GraphQLHTTP
class TestGraphQLServer:
@pytest.fixture
def schema(self):
"""Create a test schema."""
def resolve_hello(obj, info, name="World"):
return f"Hello, {name}!"
return GraphQLSchema(
query=GraphQLObjectType(
name="Query",
fields={
"hello": GraphQLField(
GraphQLString,
args={"name": GraphQLArgument(GraphQLString)},
resolve=resolve_hello
)
}
)
)
@pytest.fixture
def server(self, schema):
"""Create test server."""
return GraphQLHTTP(schema=schema)
@pytest.fixture
def client(self, server):
"""Create test client."""
return server.client()
def test_basic_query(self, client):
"""Test basic GraphQL query."""
response = client.post(
"/graphql",
json={"query": "{ hello }"}
)
assert response.status_code == 200
assert response.json() == {"data": {"hello": "Hello, World!"}}
def test_query_with_variables(self, client):
"""Test query with variables."""
response = client.post(
"/graphql",
json={
"query": "query GetHello($name: String) { hello(name: $name) }",
"variables": {"name": "Test"}
}
)
assert response.status_code == 200
assert response.json() == {"data": {"hello": "Hello, Test!"}}
def test_get_request(self, client):
"""Test GET request."""
response = client.get("/graphql?query={hello}")
assert response.status_code == 200
assert response.json() == {"data": {"hello": "Hello, World!"}}
def test_invalid_query(self, client):
"""Test invalid GraphQL syntax."""
response = client.post(
"/graphql",
json={"query": "{ invalid_syntax }"}
)
assert response.status_code == 200
result = response.json()
assert "errors" in result
def test_health_check(self, client):
"""Test health check endpoint."""
# Create server with health check
server = GraphQLHTTP(schema=self.schema(), health_path="/health")
client = server.client()
response = client.get("/health")
assert response.status_code == 200
assert response.text == "OK"Testing with Authentication #
Test JWT authentication using mock tokens:
import pytest
from unittest.mock import patch, MagicMock
from graphql import GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
from graphql_http import GraphQLHTTP
class TestAuthentication:
@pytest.fixture
def schema(self):
return GraphQLSchema(
query=GraphQLObjectType(
name="Query",
fields={
"protected": GraphQLField(
GraphQLString,
resolve=lambda *_: "Protected data"
)
}
)
)
@pytest.fixture
def auth_server(self, schema):
return GraphQLHTTP(
schema=schema,
auth_enabled=True,
auth_jwks_uri="https://example.com/.well-known/jwks.json",
auth_issuer="https://example.com/",
auth_audience="test-audience"
)
def test_missing_auth_header(self, auth_server):
"""Test request without auth header."""
client = auth_server.client()
response = client.post(
"/graphql",
json={"query": "{ protected }"}
)
assert response.status_code == 401
assert "Unauthorized" in response.json()["errors"][0]["message"]
@patch('jwt.decode')
@patch('jwt.PyJWKClient')
def test_valid_token(self, mock_jwks_client, mock_jwt_decode, auth_server):
"""Test request with valid JWT token."""
# Mock JWKS client
mock_client = MagicMock()
mock_signing_key = MagicMock()
mock_signing_key.key = "mock_key"
mock_client.get_signing_key_from_jwt.return_value = mock_signing_key
mock_jwks_client.return_value = mock_client
# Mock JWT decode
mock_jwt_decode.return_value = {
"sub": "user123",
"aud": "test-audience",
"iss": "https://example.com/"
}
client = auth_server.client()
response = client.post(
"/graphql",
json={"query": "{ protected }"},
headers={"Authorization": "Bearer valid_token"}
)
assert response.status_code == 200
assert response.json() == {"data": {"protected": "Protected data"}}
def test_introspection_without_auth(self, auth_server):
"""Test that introspection works without auth by default."""
client = auth_server.client()
response = client.post(
"/graphql",
json={"query": "{ __schema { queryType { name } } }"}
)
assert response.status_code == 200
assert "data" in response.json()Testing CORS #
Test CORS functionality:
class TestCORS:
@pytest.fixture
def cors_server(self, schema):
return GraphQLHTTP(schema=schema, allow_cors=True)
def test_cors_preflight(self, cors_server):
"""Test CORS preflight request."""
client = cors_server.client()
response = client.options(
"/graphql",
headers={
"Origin": "https://example.com",
"Access-Control-Request-Method": "POST",
"Access-Control-Request-Headers": "Content-Type"
}
)
assert response.status_code == 200
assert "Access-Control-Allow-Origin" in response.headers
assert "Access-Control-Allow-Methods" in response.headers
def test_cors_actual_request(self, cors_server):
"""Test actual CORS request."""
client = cors_server.client()
response = client.post(
"/graphql",
json={"query": "{ hello }"},
headers={"Origin": "https://example.com"}
)
assert response.status_code == 200
# CORS headers are added by Starlette middlewareTesting GraphiQL Interface #
Test the GraphiQL interface:
class TestGraphiQL:
def test_graphiql_interface(self, client):
"""Test GraphiQL interface is served."""
response = client.get(
"/graphql",
headers={"Accept": "text/html"}
)
assert response.status_code == 200
assert "text/html" in response.headers["content-type"]
assert "GraphiQL" in response.text
def test_graphiql_disabled(self, schema):
"""Test with GraphiQL disabled."""
server = GraphQLHTTP(schema=schema, serve_graphiql=False)
client = server.client()
response = client.get(
"/graphql",
headers={"Accept": "text/html"}
)
# Should return JSON instead of HTML
assert response.status_code == 400 # No query provided
def test_raw_parameter(self, client):
"""Test raw parameter disables GraphiQL."""
response = client.get(
"/graphql?query={hello}&raw=true",
headers={"Accept": "text/html"}
)
assert response.status_code == 200
assert response.headers["content-type"] == "application/json"Testing Custom Middleware #
Test custom middleware functionality:
class TestMiddleware:
def test_logging_middleware(self, schema):
"""Test custom logging middleware."""
logs = []
def logging_middleware(next_fn, root, info, **args):
logs.append(f"Field: {info.field_name}")
return next_fn(root, info, **args)
server = GraphQLHTTP(
schema=schema,
middleware=[logging_middleware]
)
client = server.client()
response = client.post("/graphql", json={"query": "{ hello }"})
assert response.status_code == 200
assert "Field: hello" in logs
def test_authentication_middleware(self, schema):
"""Test custom authentication middleware."""
def auth_middleware(next_fn, root, info, **args):
# Simulate checking authentication
if info.field_name == "protected":
raise Exception("Authentication required")
return next_fn(root, info, **args)
protected_schema = GraphQLSchema(
query=GraphQLObjectType(
name="Query",
fields={
"public": GraphQLField(
GraphQLString,
resolve=lambda *_: "Public data"
),
"protected": GraphQLField(
GraphQLString,
resolve=lambda *_: "Protected data"
)
}
)
)
server = GraphQLHTTP(
schema=protected_schema,
middleware=[auth_middleware]
)
client = server.client()
# Public field should work
response = client.post("/graphql", json={"query": "{ public }"})
assert response.status_code == 200
assert response.json()["data"]["public"] == "Public data"
# Protected field should fail
response = client.post("/graphql", json={"query": "{ protected }"})
assert response.status_code == 200
assert "errors" in response.json()Performance Testing #
Test server performance:
import time
import concurrent.futures
class TestPerformance:
def test_concurrent_requests(self, client):
"""Test handling concurrent requests."""
def make_request():
response = client.post("/graphql", json={"query": "{ hello }"})
return response.status_code == 200
# Make 10 concurrent requests
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(make_request) for _ in range(10)]
results = [future.result() for future in futures]
assert all(results) # All requests should succeed
def test_response_time(self, client):
"""Test response time is reasonable."""
start_time = time.time()
response = client.post("/graphql", json={"query": "{ hello }"})
end_time = time.time()
assert response.status_code == 200
assert (end_time - start_time) < 1.0 # Should respond within 1 secondIntegration Testing #
Test integration with external services:
import pytest
from unittest.mock import patch
class TestIntegration:
@patch('your_module.database.get_user')
def test_database_integration(self, mock_get_user, client):
"""Test database integration."""
# Mock database response
mock_get_user.return_value = {
"id": 1,
"name": "Test User",
"email": "test@example.com"
}
response = client.post(
"/graphql",
json={"query": "{ user(id: 1) { name email } }"}
)
assert response.status_code == 200
data = response.json()["data"]
assert data["user"]["name"] == "Test User"
mock_get_user.assert_called_once_with(1)
@patch('requests.get')
def test_external_api_integration(self, mock_get, client):
"""Test external API integration."""
# Mock external API response
mock_get.return_value.json.return_value = {
"weather": "sunny",
"temperature": 25
}
mock_get.return_value.status_code = 200
response = client.post(
"/graphql",
json={"query": "{ weather { condition temperature } }"}
)
assert response.status_code == 200
# Verify external API was called
mock_get.assert_called_once()Test Configuration #
Example pytest.ini configuration:
[tool:pytest]
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
addopts =
-v
--tb=short
--strict-markers
markers =
integration: Integration tests
unit: Unit tests
auth: Authentication testsRunning Tests #
# Run all tests
pytest
# Run specific test file
pytest tests/test_server.py
# Run tests with coverage
pytest --cov=graphql_http
# Run only unit tests
pytest -m unit
# Run tests in parallel
pytest -n autoThis comprehensive testing approach ensures your GraphQL HTTP server works correctly across all features and scenarios.