Skip to content

Commit 2308965

Browse files
authored
Extract query function from GraphQLTestCase making it possible to use in a pytest fixture (#1015)
1 parent 97de26b commit 2308965

File tree

3 files changed

+132
-34
lines changed

3 files changed

+132
-34
lines changed

docs/testing.rst

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Testing API calls with django
22
=============================
33

4+
Using unittest
5+
--------------
6+
47
If you want to unittest your API calls derive your test case from the class `GraphQLTestCase`.
58

69
Your endpoint is set through the `GRAPHQL_URL` attribute on `GraphQLTestCase`. The default endpoint is `GRAPHQL_URL = "/graphql/"`.
@@ -12,12 +15,8 @@ Usage:
1215
import json
1316
1417
from graphene_django.utils.testing import GraphQLTestCase
15-
from my_project.config.schema import schema
1618
1719
class MyFancyTestCase(GraphQLTestCase):
18-
# Here you need to inject your test case's schema
19-
GRAPHQL_SCHEMA = schema
20-
2120
def test_some_query(self):
2221
response = self.query(
2322
'''
@@ -82,3 +81,38 @@ Usage:
8281
8382
# Add some more asserts if you like
8483
...
84+
85+
Using pytest
86+
------------
87+
88+
To use pytest define a simple fixture using the query helper below
89+
90+
.. code:: python
91+
92+
# Create a fixture using the graphql_query helper and `client` fixture from `pytest-django`.
93+
import pytest
94+
from graphene_django.utils.testing import graphql_query
95+
96+
@pytest.fixture
97+
def client_query(client)
98+
def func(*args, **kwargs):
99+
return graphql_query(*args, **kwargs, client=client)
100+
101+
return func
102+
103+
# Test you query using the client_query fixture
104+
def test_some_query(client_query):
105+
response = graphql_query(
106+
'''
107+
query {
108+
myModel {
109+
id
110+
name
111+
}
112+
}
113+
''',
114+
op_name='myModel'
115+
)
116+
117+
content = json.loads(response.content)
118+
assert 'errors' not in content

graphene_django/tests/test_utils.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from ..utils import camelize, get_model_fields, GraphQLTestCase
88
from .models import Film, Reporter
9+
from ..utils.testing import graphql_query
910

1011

1112
def test_get_model_fields_no_duplication():
@@ -58,3 +59,29 @@ def runTest(self):
5859
"operationName",
5960
"QueryName",
6061
) in body.items(), "Field 'operationName' is not present in the final request."
62+
63+
64+
@pytest.mark.django_db
65+
@patch("graphene_django.utils.testing.Client.post")
66+
def test_graphql_query_case_op_name(post_mock):
67+
graphql_query("query { }", op_name="QueryName")
68+
body = json.loads(post_mock.call_args.args[1])
69+
# `operationName` field from https://graphql.org/learn/serving-over-http/#post-request
70+
assert (
71+
"operationName",
72+
"QueryName",
73+
) in body.items(), "Field 'operationName' is not present in the final request."
74+
75+
76+
@pytest.fixture
77+
def client_query(client):
78+
def func(*args, **kwargs):
79+
return graphql_query(*args, client=client, **kwargs)
80+
81+
return func
82+
83+
84+
def test_pytest_fixture_usage(client_query):
85+
response = graphql_query("query { test }")
86+
content = json.loads(response.content)
87+
assert content == {"data": {"test": "Hello World"}}

graphene_django/utils/testing.py

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,76 @@
22

33
from django.test import TestCase, Client
44

5+
DEFAULT_GRAPHQL_URL = "/graphql/"
6+
7+
8+
def graphql_query(
9+
query,
10+
op_name=None,
11+
input_data=None,
12+
variables=None,
13+
headers=None,
14+
client=None,
15+
graphql_url=None,
16+
):
17+
"""
18+
Args:
19+
query (string) - GraphQL query to run
20+
op_name (string) - If the query is a mutation or named query, you must
21+
supply the op_name. For annon queries ("{ ... }"),
22+
should be None (default).
23+
input_data (dict) - If provided, the $input variable in GraphQL will be set
24+
to this value. If both ``input_data`` and ``variables``,
25+
are provided, the ``input`` field in the ``variables``
26+
dict will be overwritten with this value.
27+
variables (dict) - If provided, the "variables" field in GraphQL will be
28+
set to this value.
29+
headers (dict) - If provided, the headers in POST request to GRAPHQL_URL
30+
will be set to this value.
31+
client (django.test.Client) - Test client. Defaults to django.test.Client.
32+
graphql_url (string) - URL to graphql endpoint. Defaults to "/graphql".
33+
34+
Returns:
35+
Response object from client
36+
"""
37+
if client is None:
38+
client = Client()
39+
if not graphql_url:
40+
graphql_url = DEFAULT_GRAPHQL_URL
41+
42+
body = {"query": query}
43+
if op_name:
44+
body["operationName"] = op_name
45+
if variables:
46+
body["variables"] = variables
47+
if input_data:
48+
if variables in body:
49+
body["variables"]["input"] = input_data
50+
else:
51+
body["variables"] = {"input": input_data}
52+
if headers:
53+
resp = client.post(
54+
graphql_url, json.dumps(body), content_type="application/json", **headers
55+
)
56+
else:
57+
resp = client.post(
58+
graphql_url, json.dumps(body), content_type="application/json"
59+
)
60+
return resp
61+
562

663
class GraphQLTestCase(TestCase):
764
"""
865
Based on: https://www.sam.today/blog/testing-graphql-with-graphene-django/
966
"""
1067

1168
# URL to graphql endpoint
12-
GRAPHQL_URL = "/graphql/"
13-
# Here you need to set your graphql schema for the tests
14-
GRAPHQL_SCHEMA = None
69+
GRAPHQL_URL = DEFAULT_GRAPHQL_URL
1570

1671
@classmethod
1772
def setUpClass(cls):
1873
super(GraphQLTestCase, cls).setUpClass()
1974

20-
if not cls.GRAPHQL_SCHEMA:
21-
raise AttributeError(
22-
"Variable GRAPHQL_SCHEMA not defined in GraphQLTestCase."
23-
)
24-
2575
cls._client = Client()
2676

2777
def query(self, query, op_name=None, input_data=None, variables=None, headers=None):
@@ -43,28 +93,15 @@ def query(self, query, op_name=None, input_data=None, variables=None, headers=No
4393
Returns:
4494
Response object from client
4595
"""
46-
body = {"query": query}
47-
if op_name:
48-
body["operationName"] = op_name
49-
if variables:
50-
body["variables"] = variables
51-
if input_data:
52-
if variables in body:
53-
body["variables"]["input"] = input_data
54-
else:
55-
body["variables"] = {"input": input_data}
56-
if headers:
57-
resp = self._client.post(
58-
self.GRAPHQL_URL,
59-
json.dumps(body),
60-
content_type="application/json",
61-
**headers
62-
)
63-
else:
64-
resp = self._client.post(
65-
self.GRAPHQL_URL, json.dumps(body), content_type="application/json"
66-
)
67-
return resp
96+
return graphql_query(
97+
query,
98+
op_name=op_name,
99+
input_data=input_data,
100+
variables=variables,
101+
headers=headers,
102+
client=self._client,
103+
graphql_url=self.GRAPHQL_URL,
104+
)
68105

69106
def assertResponseNoErrors(self, resp):
70107
"""

0 commit comments

Comments
 (0)