Hypothesis-GraphQL
Hypothesis-GraphQL provides Hypothesis strategies for generating arbitrary GraphQL queries that conform to a given schema. This Python library is crucial for property-based testing of GraphQL backend implementations, helping to uncover edge cases and validate server behavior against a wide range of valid and invalid inputs. It is actively maintained, with regular updates.
Warnings
- gotcha When using custom scalar types in your GraphQL schema (e.g., `Date`, `UUID`), `hypothesis-graphql`'s `from_schema` function requires you to provide explicit Hypothesis strategies for generating the corresponding GraphQL AST nodes. Failing to do so will result in errors when trying to generate data for these custom types.
- gotcha GraphQL fields are nullable by default. This can hide underlying server errors, as a field might simply return `null` instead of indicating a problem with the data fetching. Explicitly defining fields as non-nullable (`!`) in your schema is crucial for fields that should always return a value, forcing an error if data is missing or invalid. `hypothesis-graphql` will respect these non-null constraints.
- gotcha GraphQL servers typically return an HTTP 200 OK status code even if the GraphQL query itself contains errors (which are then listed in the `errors` field of the JSON response body). Clients consuming your API cannot rely solely on HTTP status codes for error detection, which can be a common footgun in client-side error handling.
- gotcha While GraphQL aims to prevent over-fetching, poorly designed schemas or client queries (especially deeply nested ones or those requesting many fields on large collections) can lead to 'N+1' query problems, excessive database joins, or high query complexity, resulting in performance bottlenecks. `hypothesis-graphql` can generate such complex queries, which may expose these performance issues in your backend.
- breaking Making seemingly minor changes to your GraphQL schema's type system can constitute a breaking change for clients, even if data values are compatible. Examples include changing a scalar type (e.g., from a custom `Metadata` scalar to `JSON` scalar, or `String` to `ID`) or removing a value from an enum. GraphQL's nominal typing means clients expecting one type will break on another, regardless of data compatibility.
Install
-
pip install hypothesis-graphql
Imports
- from_schema
from hypothesis_graphql import from_schema
- queries
from hypothesis_graphql import queries
- mutations
from hypothesis_graphql import mutations
- nodes
from hypothesis_graphql import nodes
Quickstart
from hypothesis import given
from hypothesis_graphql import from_schema
import requests
import os
# Define a simple GraphQL schema
SCHEMA = """
type Book {
title: String
author: Author
}
type Author {
name: String
books: [Book]
}
type Query {
getBooks: [Book]
getAuthors: [Author]
}
type Mutation {
addBook(title: String!, author: String!): Book!
addAuthor(name: String!): Author!
}
"""
# Replace with your actual GraphQL endpoint
GRAPHQL_ENDPOINT = os.environ.get('GRAPHQL_TEST_ENDPOINT', 'http://127.0.0.1:8000/graphql')
@given(from_schema(SCHEMA))
def test_graphql_api(query):
"""Tests a GraphQL endpoint with generated queries."""
print(f"\nGenerated query:\n{query}")
try:
response = requests.post(GRAPHQL_ENDPOINT, json={"query": query})
response.raise_for_status() # Raise an exception for HTTP errors
json_response = response.json()
# Assert no GraphQL errors, unless specifically testing negative cases
if json_response.get("errors"):
print(f"GraphQL Errors: {json_response['errors']}")
# Further assertions can go here based on expected data or error types
assert response.status_code == 200
except requests.exceptions.ConnectionError:
print(f"Warning: Could not connect to GraphQL endpoint at {GRAPHQL_ENDPOINT}. "
"Please ensure your GraphQL server is running.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
raise
# To run the test (e.g., if not using pytest):
# if __name__ == '__main__':
# # Note: Directly calling @given decorated functions runs a single example.
# # For property-based testing, run with pytest.
# print("Running a single generated example. For full property testing, use pytest.")
# test_graphql_api()