Skip to content

Commit a455edf

Browse files
committed
Tests: Improve type coverage
Replicates graphql/graphql-js@e45fb12
1 parent 7d826f0 commit a455edf

9 files changed

+68
-38
lines changed

tests/execution/test_directives.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from graphql import GraphQLSchema
2-
from graphql.execution import execute
1+
from graphql.execution import execute, ExecutionResult
32
from graphql.language import parse
4-
from graphql.type import GraphQLObjectType, GraphQLField, GraphQLString
3+
from graphql.pyutils import AwaitableOrValue
4+
from graphql.type import GraphQLObjectType, GraphQLField, GraphQLSchema, GraphQLString
55

66
schema = GraphQLSchema(
77
GraphQLObjectType(
@@ -19,7 +19,7 @@ def b(self, *_args):
1919
return "b"
2020

2121

22-
def execute_test_query(query):
22+
def execute_test_query(query: str) -> AwaitableOrValue[ExecutionResult]:
2323
document = parse(query)
2424
return execute(schema, document, RootValue())
2525

tests/execution/test_executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def e(self, _info):
112112
f = "Fish"
113113

114114
# Called only by DataType::pic static resolver
115-
def pic(self, _info, size):
115+
def pic(self, _info, size: int):
116116
return f"Pic of size: {size}"
117117

118118
def deep(self, _info):

tests/execution/test_lists.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from collections import namedtuple
22
from gc import collect
3+
from typing import Any
34

45
from pytest import mark # type: ignore
56

@@ -10,6 +11,7 @@
1011
GraphQLList,
1112
GraphQLNonNull,
1213
GraphQLObjectType,
14+
GraphQLOutputType,
1315
GraphQLSchema,
1416
GraphQLString,
1517
)
@@ -44,18 +46,20 @@ def get_response(test_type, test_data):
4446
)
4547

4648

47-
def check_response(response, expected):
49+
def check_response(response: Any, expected: Any) -> None:
4850
if not response.errors:
4951
response = response.data
5052
assert response == expected
5153

5254

53-
def check(test_type, test_data, expected):
55+
def check(test_type: GraphQLOutputType, test_data: Any, expected: Any) -> None:
5456

5557
check_response(get_response(test_type, test_data), expected)
5658

5759

58-
async def check_async(test_type, test_data, expected):
60+
async def check_async(
61+
test_type: GraphQLOutputType, test_data: Any, expected: Any
62+
) -> None:
5963
check_response(await get_response(test_type, test_data), expected)
6064

6165
# Note: When Array values are rejected asynchronously,

tests/execution/test_nonnull.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import re
22
from inspect import isawaitable
3+
from typing import Any, Awaitable, cast
34

45
from pytest import mark # type: ignore
56

6-
from graphql.execution import execute
7+
from graphql.execution import execute, ExecutionResult
78
from graphql.language import parse
9+
from graphql.pyutils import AwaitableOrValue
810
from graphql.type import (
911
GraphQLArgument,
1012
GraphQLField,
@@ -95,22 +97,25 @@ async def promiseNonNullNest(self, _info):
9597
)
9698

9799

98-
def execute_query(query, root_value):
100+
def execute_query(query: str, root_value: Any) -> AwaitableOrValue[ExecutionResult]:
99101
return execute(schema=schema, document=parse(query), root_value=root_value)
100102

101103

102104
# avoids also doing any nests
103-
def patch(data):
105+
def patch(data: str) -> str:
104106
return re.sub(
105107
r"\bsyncNonNull\b", "promiseNonNull", re.sub(r"\bsync\b", "promise", data)
106108
)
107109

108110

109-
async def execute_sync_and_async(query, root_value):
111+
async def execute_sync_and_async(query: str, root_value: Any) -> ExecutionResult:
110112
sync_result = execute_query(query, root_value)
111113
if isawaitable(sync_result):
112-
sync_result = await sync_result
113-
async_result = await execute_query(patch(query), root_value)
114+
sync_result = await cast(Awaitable[ExecutionResult], sync_result)
115+
sync_result = cast(ExecutionResult, sync_result)
116+
async_result = await cast(
117+
Awaitable[ExecutionResult], execute_query(patch(query), root_value)
118+
)
114119

115120
assert repr(async_result) == patch(repr(sync_result))
116121
return sync_result
@@ -254,12 +259,16 @@ def describe_nulls_a_complex_tree_of_nullable_fields_each():
254259

255260
@mark.asyncio
256261
async def returns_null():
257-
result = await execute_query(query, NullingData())
262+
result = await cast(
263+
Awaitable[ExecutionResult], execute_query(query, NullingData())
264+
)
258265
assert result == (data, None)
259266

260267
@mark.asyncio
261268
async def throws():
262-
result = await execute_query(query, ThrowingData())
269+
result = await cast(
270+
Awaitable[ExecutionResult], execute_query(query, ThrowingData())
271+
)
263272
assert result == (
264273
data,
265274
[
@@ -384,7 +393,9 @@ def describe_nulls_first_nullable_after_long_chain_of_non_null_fields():
384393

385394
@mark.asyncio
386395
async def returns_null():
387-
result = await execute_query(query, NullingData())
396+
result = await cast(
397+
Awaitable[ExecutionResult], execute_query(query, NullingData())
398+
)
388399
assert result == (
389400
data,
390401
[
@@ -445,7 +456,9 @@ async def returns_null():
445456

446457
@mark.asyncio
447458
async def throws():
448-
result = await execute_query(query, ThrowingData())
459+
result = await cast(
460+
Awaitable[ExecutionResult], execute_query(query, ThrowingData())
461+
)
449462
assert result == (
450463
data,
451464
[

tests/execution/test_resolve.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from collections import ChainMap
2+
from typing import Any, Optional
23

34
from graphql import graphql_sync
45
from graphql.error import GraphQLError
6+
from graphql.execution import ExecutionResult
57
from graphql.language import SourceLocation
68
from graphql.type import (
79
GraphQLArgument,
@@ -16,7 +18,7 @@
1618

1719

1820
def describe_execute_resolve_function():
19-
def _test_schema(test_field):
21+
def _test_schema(test_field: GraphQLField) -> GraphQLSchema:
2022
return GraphQLSchema(GraphQLObjectType("Query", {"test": test_field}))
2123

2224
def default_function_accesses_attributes():
@@ -68,7 +70,7 @@ class Adder:
6870
def __init__(self, num):
6971
self._num = num
7072

71-
def test(self, info, addend1):
73+
def test(self, info, addend1: int):
7274
return self._num + addend1 + info.context.addend2
7375

7476
root_value = Adder(700)
@@ -137,7 +139,7 @@ def transforms_arguments_using_out_names():
137139
)
138140
)
139141

140-
def execute(source, root_value=None):
142+
def execute(source: str, root_value: Optional[Any] = None) -> ExecutionResult:
141143
return graphql_sync(schema=schema, source=source, root_value=root_value)
142144

143145
assert execute("{ test }") == ({"test": "[None, {}]"}, None)

tests/execution/test_schema.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def executes_using_a_schema():
1919
class Article:
2020

2121
# noinspection PyShadowingBuiltins
22-
def __init__(self, id):
22+
def __init__(self, id: int):
2323
self.id = id
2424
self.isPublished = True
2525
self.author = JohnSmith()
@@ -77,7 +77,7 @@ def __init__(self, id):
7777
"article": GraphQLField(
7878
BlogArticle,
7979
args={"id": GraphQLArgument(GraphQLID)},
80-
resolve=lambda obj, info, id: Article(id),
80+
resolve=lambda _obj, _info, id: Article(id),
8181
),
8282
"feed": GraphQLField(
8383
GraphQLList(BlogArticle),
@@ -90,19 +90,19 @@ def __init__(self, id):
9090

9191
# noinspection PyPep8Naming,PyMethodMayBeStatic
9292
class Author:
93-
def pic(self, info_, width, height):
93+
def pic(self, info_, width: int, height: int) -> "Pic":
9494
return Pic(123, width, height)
9595

9696
@property
97-
def recentArticle(self):
97+
def recentArticle(self) -> Article:
9898
return Article(1)
9999

100100
class JohnSmith(Author):
101101
id = 123
102102
name = "John Smith"
103103

104104
class Pic:
105-
def __init__(self, uid, width, height):
105+
def __init__(self, uid: int, width: int, height: int):
106106
self.url = f"cdn://{uid}"
107107
self.width = f"{width}"
108108
self.height = f"{height}"

tests/star_wars_data.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
demo.
66
"""
77

8-
from typing import Awaitable, Collection, Iterator
8+
from typing import Awaitable, Collection, Dict, Iterator, Optional
99

1010
__all__ = ["get_droid", "get_friends", "get_hero", "get_human", "get_secret_backstory"]
1111

@@ -80,7 +80,13 @@ def __init__(self, id, name, friends, appearsIn, primaryFunction):
8080
id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None
8181
)
8282

83-
human_data = {"1000": luke, "1001": vader, "1002": han, "1003": leia, "1004": tarkin}
83+
human_data: Dict[str, Human] = {
84+
"1000": luke,
85+
"1001": vader,
86+
"1002": han,
87+
"1003": leia,
88+
"1004": tarkin,
89+
}
8490

8591
threepio = Droid(
8692
id="2000",
@@ -98,17 +104,17 @@ def __init__(self, id, name, friends, appearsIn, primaryFunction):
98104
primaryFunction="Astromech",
99105
)
100106

101-
droid_data = {"2000": threepio, "2001": artoo}
107+
droid_data: Dict[str, Droid] = {"2000": threepio, "2001": artoo}
102108

103109

104110
# noinspection PyShadowingBuiltins
105-
async def get_character(id: str) -> Character:
111+
async def get_character(id: str) -> Optional[Character]:
106112
"""Helper function to get a character by ID."""
107113
# We use an async function just to illustrate that GraphQL-core supports it.
108-
return human_data.get(id) or droid_data.get(id) # type: ignore
114+
return human_data.get(id) or droid_data.get(id)
109115

110116

111-
def get_friends(character: Character) -> Iterator[Awaitable[Character]]:
117+
def get_friends(character: Character) -> Iterator[Awaitable[Optional[Character]]]:
112118
"""Allows us to query for a character's friends."""
113119
# Notice that GraphQL-core accepts iterators of awaitables.
114120
return map(get_character, character.friends)
@@ -124,15 +130,15 @@ def get_hero(episode: int) -> Character:
124130

125131

126132
# noinspection PyShadowingBuiltins
127-
def get_human(id: str) -> Human:
133+
def get_human(id: str) -> Optional[Human]:
128134
"""Allows us to query for the human with the given id."""
129-
return human_data.get(id) # type: ignore
135+
return human_data.get(id)
130136

131137

132138
# noinspection PyShadowingBuiltins
133-
def get_droid(id: str) -> Droid:
139+
def get_droid(id: str) -> Optional[Droid]:
134140
"""Allows us to query for the droid with the given id."""
135-
return droid_data.get(id) # type: ignore
141+
return droid_data.get(id)
136142

137143

138144
# noinspection PyUnusedLocal

tests/test_star_wars_introspection.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
from typing import Any
2+
13
from graphql import graphql_sync
24

35
from .star_wars_schema import star_wars_schema
46

57

6-
def query_star_wars(source):
8+
def query_star_wars(source: str) -> Any:
79
result = graphql_sync(star_wars_schema, source)
810
assert result.errors is None
911
return result.data

tests/test_star_wars_validation.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
from typing import List
2+
3+
from graphql.error import GraphQLError
14
from graphql.language import parse, Source
25
from graphql.validation import validate
36

47
from .star_wars_schema import star_wars_schema
58

69

7-
def validation_errors(query):
10+
def validation_errors(query: str) -> List[GraphQLError]:
811
"""Helper function to test a query and the expected response."""
912
source = Source(query, "StarWars.graphql")
1013
ast = parse(source)

0 commit comments

Comments
 (0)