Strawberry code-first GraphQL schema tested offline: working versions (tested 2026-10-18)
Define code-first GraphQL types, queries, mutations and input types, then execute the schema in-process with context using strawberry and pytest.
passed5 passed · 0 failed2026-10-18 03:57 UTC- Status
- passed
- Last passed
- 2026-10-18 03:57 UTC
- Tests
- 5 / 5
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions3
strawberry-graphql@0.336.0pytest-asyncio@1.4.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 39095370682
The code
Exactly the files that passed. Your agent can pull them with get_recipe("strawberry-graphql-schema-tests") (install), or see them on GitHub.
requirements.txt
strawberry-graphql==0.336.0 pytest-asyncio==1.4.0 pytest==9.1.1
schema.py
from dataclasses import dataclass, field
import strawberry
@strawberry.type
class User:
id: strawberry.ID
name: str
email: str
@strawberry.field
def display_name(self) -> str:
# exposed as "displayName" in the schema by strawberry's auto camel-casing
return self.name.title()
@strawberry.input
class CreateUserInput:
name: str
email: str
@dataclass
class Store:
users: dict[str, User] = field(default_factory=dict)
seq: int = 0
def add(self, name: str, email: str) -> User:
self.seq += 1
user = User(id=strawberry.ID(str(self.seq)), name=name, email=email)
self.users[str(self.seq)] = user
return user
@strawberry.type
class Query:
@strawberry.field
def user(self, info: strawberry.Info[Store, None], id: strawberry.ID) -> User | None:
return info.context.users.get(str(id))
@strawberry.field
async def users(self, info: strawberry.Info[Store, None]) -> list[User]:
return list(info.context.users.values())
@strawberry.type
class Mutation:
@strawberry.mutation
def create_user(self, info: strawberry.Info[Store, None], input: CreateUserInput) -> User:
return info.context.add(input.name, input.email)
schema = strawberry.Schema(query=Query, mutation=Mutation)
tests/test_schema.py
import pytest
from schema import Store, schema
@pytest.fixture
def store():
s = Store()
s.add("Ada Lovelace", "[email protected]")
return s
async def run(query, store, variables=None):
return await schema.execute(query, variable_values=variables, context_value=store)
async def test_query_reads_context(store):
result = await run('{ user(id: "1") { id name } }', store)
assert result.errors is None
assert result.data == {"user": {"id": "1", "name": "Ada Lovelace"}}
async def test_resolver_field_is_camel_cased(store):
# the Python field is display_name but the schema exposes displayName
result = await run('{ user(id: "1") { displayName } }', store)
assert result.errors is None
assert result.data["user"]["displayName"] == "Ada Lovelace"
async def test_mutation_with_variables(store):
query = """
mutation Create($input: CreateUserInput!) {
createUser(input: $input) { id name email }
}
"""
result = await run(query, store, {"input": {"name": "Grace Hopper", "email": "[email protected]"}})
assert result.errors is None
assert result.data["createUser"]["name"] == "Grace Hopper"
assert len(store.users) == 2
async def test_missing_input_field_is_a_validation_error(store):
result = await run('mutation { createUser(input: {name: "x"}) { id } }', store)
assert result.data is None
assert result.errors is not None
assert "email" in str(result.errors)
async def test_async_resolver_needs_async_execute(store):
result = await run("{ users { name } }", store)
assert result.errors is None
assert [u["name"] for u in result.data["users"]] == ["Ada Lovelace"]
# execute_sync does not raise here, it returns an error result instead
sync = schema.execute_sync("{ users { name } }", context_value=store)
assert sync.data is None
assert "synchronously" in str(sync.errors)
pytest.ini
[pytest]
pythonpath = .
asyncio_mode = auto
filterwarnings =
ignore:coroutine .* was never awaited:RuntimeWarning
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.