pytest-graphql
The GraphQL test client for Python whose calls are built from the schema and checked against it before they are sent.
A test names an operation, and the client chooses the fields to ask for. A test has no query text, and a schema change does not mean editing query strings by hand.
Documentation: https://skhomenko.github.io/pytest-graphql/
Use it when
- You test a GraphQL API from Python, over HTTP, against a server that is running.
- You want a wrong operation, argument or field name to fail before any request is sent, with a message that names the closest correct one.
- Your schema changes often, and you do not want to edit query strings in tests.
- A coding agent writes or updates your GraphQL tests.
- You want a failed test to show its calls and a
curlline that repeats the failed call.
Do not use it when
- Your API is REST. Use an HTTP client or a test tool made for REST.
- You write production client code. Use a general GraphQL client library.
- You need a mock GraphQL server. This package calls a server that you have. Use a mock server tool to fake one.
- You want to fuzz every operation of an API. Use a dedicated property-based API fuzzer.
Quickstart
Install the package with its pytest extra:
pip install "pytest-graphql[pytest]"
Tell pytest where your GraphQL endpoint is:
# pytest.ini
[pytest]
gql_url = http://localhost:8000/graphql
Write a test. The gql fixture is a client that has already read your schema:
def test_user_has_a_name(gql):
user = gql.query("user", id="u1")
assert user.name == "Ada Lovelace"
Run pytest. The client chooses the fields to ask for, so the test contains no
query text.
A fuller example
def test_a_new_post_has_the_title_that_was_sent(gql):
payload = gql.fake.CreatePostInput(author_id="u1")
post = gql.mutation("createPost", input=payload)
assert post == gql.expect.Post(title=payload["title"])
def test_a_user_with_the_fields_that_the_test_needs(gql):
user = gql.query("user", id="u1", fields=["id", "name", {"team": ["name"]}])
assert user == gql.expect.User(name="Ada Lovelace", team={"name": "Core"})
def test_an_update_of_a_missing_user_fails(gql):
with gql.expect_error(path=["updateUser"]):
gql.mutation("updateUser", id="missing", name="Ada", fields=["id"])
API at a glance
| Call | What it does |
|---|---|
gql |
The pytest fixture. A client that has read your schema. |
gql.query("user", id="u1") |
Runs a query by operation name. Arguments are snake_case keywords. The client chooses the fields. |
gql.mutation("createPost", input=payload) |
Runs a mutation by operation name, with the same rules as query. |
gql.execute(document, variables) |
Sends a document that you wrote, such as one with directives or several root fields. It checks the document and returns the response. |
fields= |
Chooses exact fields for a call: a list or dictionary of names, Field objects, a raw string, or a Selection. |
Selection |
A reusable choice of fields. |
gql.expect.User(name="Ada") |
A matcher for part of an object. A failure prints a diff. |
gql.expect_error(code=...) |
A context manager for a call that must return an error. |
gql.fake.CreatePostInput() and unique() |
Seeded fake input that repeats from run to run. unique() marks a field that must differ. |
gql.as_(token) and gql.with_headers(...) |
A clone of the client with another identity, or with extra headers. |
gql.wait_until("post", until=..., id=...) |
Repeats a query until a condition holds, or the deadline passes. |
build_client(url=...) |
A client for scripts and for unittest, with no pytest. |
Compared with other tools
| Approach | Use it when | What this package adds |
|---|---|---|
| Query strings sent with an HTTP library | A few tests, or a suite that is not expected to grow. | Queries built from the schema, checked before they are sent, and responses with attribute access. See Migrating from a hand-rolled client. |
| A general GraphQL client library | Code that calls a GraphQL API from an application. | A design built for tests: auto-selection, matchers with a diff, errors that raise, seeded test input and failure reports. |
| The test client of your server framework | Fast tests of resolvers, in the same process as the server. | Tests over HTTP against any running server, in any language, through the same path that real clients use. |
| A property-based API fuzzer, such as Schemathesis | You want generated cases for every operation of an API. | Tests that you write for the behavior of one operation, with calls built from the schema and checked before they are sent. It does not generate cases for a whole API. |
Features
- Fields chosen for you. Auto-selection builds the selection set from the
schema, and
fields=takes over when you need exact fields. Selections - Responses that read like Python. Attribute access, snake_case names, lists with helpers, and an error for a field that is not there. Responses
- Assertions with a readable diff. Plain
assert, partial matchers such asgql.expect.User(...), and list helpers. Assertions - Errors that raise. A response with errors raises, and
gql.expect_error()is for the test that expects one. Errors - Seeded fake input.
gql.fake.<InputType>()repeats from run to run, andunique()marks a field that must differ. Factory - Identity. Clone a client as another user, write your own
Auth, and refresh a token. Authentication - Polling.
gql.wait_until()repeats a query with a deadline and a backoff. Polling - Reports with the calls in them. A failed test lists its calls, hides the
headers and variables that you list as secrets, and ends with a
curlline that repeats the failed call. Diagnostics - Open to change. Replace the transport, load the schema from a file, register custom scalars, add middleware, or use one of six pytest hooks. Extending
- No pytest needed. The core imports no pytest.
build_client()works inunittest, in scripts and in a shell. Using without pytest - Parallel runs. The plugin works with
pytest-xdist. Cookbook
More documentation
- Why pytest-graphql?: what the package changes, and how it compares with other approaches.
- Using with coding agents:
rules to paste into
AGENTS.mdorCLAUDE.md, and a skill file. - Configuration: every option, flag and environment variable, and which one wins.
- Migrating from a hand-rolled client
- FAQ
- API reference
- Changelog
Install
pip install pytest-graphql
The required dependencies are graphql-core, httpx and certifi. pytest is
optional, so the client can be used outside a test suite. Add the extra when you
use the plugin:
pip install "pytest-graphql[pytest]"
Supported versions
Python 3.10 through 3.14, graphql-core 3.2 and 3.3, and pytest 7.4 or newer when the pytest extra is installed. CI tests a representative sample of that range rather than the whole cross product.
Status
Version 0.x. The API may change between minor versions, and the changelog records
every change. An on-disk schema cache, async clients, subscriptions and file
uploads are not part of 0.1.x.
License
MIT. See https://github.com/skhomenko/pytest-graphql/blob/main/LICENSE
Metadata
Release files for pytest-graphql 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pytest_graphql-0.1.1.tar.gz | 813.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pytest_graphql-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / pytest_graphql-0.1.1.tar.gz
| Download URL | pytest_graphql-0.1.1.tar.gz |
|---|---|
| Size | 813.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e012e0cd5a07ddcb5f3eeb0e0f4ae132ccc3917ecaa4dea945936b50df1ad5c3
|
|
BLAKE2b-256 checksum How to use checksums |
868b8e02cc82e7033d65be914cec7a6f854c811c915a7d25a7830668acaf15c4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency logRelease files / pytest_graphql-0.1.1-py3-none-any.whl
| Download URL | pytest_graphql-0.1.1-py3-none-any.whl |
|---|---|
| Size | 277.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f2868df19116d26588e55ed3be8866129bd6a046a82ca6cdf907a5d766464ee2
|
|
BLAKE2b-256 checksum How to use checksums |
8fe75c7ee2a18471f1f21b82bd0aa3b66fee840516c0d3295a9edd91da88fa36
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.
Transparency log