Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract query function from GraphQLTestCase making it possible to use in a pytest fixture #1015

Merged
merged 5 commits into from
Aug 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions docs/testing.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
Testing API calls with django
=============================

Using unittest
--------------

If you want to unittest your API calls derive your test case from the class `GraphQLTestCase`.

Your endpoint is set through the `GRAPHQL_URL` attribute on `GraphQLTestCase`. The default endpoint is `GRAPHQL_URL = "/graphql/"`.
Expand All @@ -12,12 +15,8 @@ Usage:
import json

from graphene_django.utils.testing import GraphQLTestCase
from my_project.config.schema import schema

class MyFancyTestCase(GraphQLTestCase):
# Here you need to inject your test case's schema
GRAPHQL_SCHEMA = schema

def test_some_query(self):
response = self.query(
'''
Expand Down Expand Up @@ -82,3 +81,38 @@ Usage:

# Add some more asserts if you like
...

Using pytest
------------

To use pytest define a simple fixture using the query helper below

.. code:: python

# Create a fixture using the graphql_query helper and `client` fixture from `pytest-django`.
import pytest
from graphene_django.utils.testing import graphql_query

@pytest.fixture
def client_query(client)
def func(*args, **kwargs):
return graphql_query(*args, **kwargs, client=client)

return func

# Test you query using the client_query fixture
def test_some_query(client_query):
response = graphql_query(
'''
query {
myModel {
id
name
}
}
''',
op_name='myModel'
)

content = json.loads(response.content)
assert 'errors' not in content
27 changes: 27 additions & 0 deletions graphene_django/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

from ..utils import camelize, get_model_fields, GraphQLTestCase
from .models import Film, Reporter
from ..utils.testing import graphql_query


def test_get_model_fields_no_duplication():
Expand Down Expand Up @@ -58,3 +59,29 @@ def runTest(self):
"operationName",
"QueryName",
) in body.items(), "Field 'operationName' is not present in the final request."


@pytest.mark.django_db
@patch("graphene_django.utils.testing.Client.post")
def test_graphql_query_case_op_name(post_mock):
graphql_query("query { }", op_name="QueryName")
body = json.loads(post_mock.call_args.args[1])
# `operationName` field from https://graphql.org/learn/serving-over-http/#post-request
assert (
"operationName",
"QueryName",
) in body.items(), "Field 'operationName' is not present in the final request."


@pytest.fixture
def client_query(client):
def func(*args, **kwargs):
return graphql_query(*args, client=client, **kwargs)

return func


def test_pytest_fixture_usage(client_query):
response = graphql_query("query { test }")
content = json.loads(response.content)
assert content == {"data": {"test": "Hello World"}}
97 changes: 67 additions & 30 deletions graphene_django/utils/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,76 @@

from django.test import TestCase, Client

DEFAULT_GRAPHQL_URL = "/graphql/"


def graphql_query(
query,
op_name=None,
input_data=None,
variables=None,
headers=None,
client=None,
graphql_url=None,
):
"""
Args:
query (string) - GraphQL query to run
op_name (string) - If the query is a mutation or named query, you must
supply the op_name. For annon queries ("{ ... }"),
should be None (default).
input_data (dict) - If provided, the $input variable in GraphQL will be set
to this value. If both ``input_data`` and ``variables``,
are provided, the ``input`` field in the ``variables``
dict will be overwritten with this value.
variables (dict) - If provided, the "variables" field in GraphQL will be
set to this value.
headers (dict) - If provided, the headers in POST request to GRAPHQL_URL
will be set to this value.
client (django.test.Client) - Test client. Defaults to django.test.Client.
graphql_url (string) - URL to graphql endpoint. Defaults to "/graphql".

Returns:
Response object from client
"""
if client is None:
client = Client()
if not graphql_url:
graphql_url = DEFAULT_GRAPHQL_URL

body = {"query": query}
if op_name:
body["operationName"] = op_name
if variables:
body["variables"] = variables
if input_data:
if variables in body:
body["variables"]["input"] = input_data
else:
body["variables"] = {"input": input_data}
if headers:
resp = client.post(
graphql_url, json.dumps(body), content_type="application/json", **headers
)
else:
resp = client.post(
graphql_url, json.dumps(body), content_type="application/json"
)
return resp


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

# URL to graphql endpoint
GRAPHQL_URL = "/graphql/"
# Here you need to set your graphql schema for the tests
GRAPHQL_SCHEMA = None
GRAPHQL_URL = DEFAULT_GRAPHQL_URL

@classmethod
def setUpClass(cls):
super(GraphQLTestCase, cls).setUpClass()

if not cls.GRAPHQL_SCHEMA:
raise AttributeError(
"Variable GRAPHQL_SCHEMA not defined in GraphQLTestCase."
)

cls._client = Client()

def query(self, query, op_name=None, input_data=None, variables=None, headers=None):
Expand All @@ -43,28 +93,15 @@ def query(self, query, op_name=None, input_data=None, variables=None, headers=No
Returns:
Response object from client
"""
body = {"query": query}
if op_name:
body["operationName"] = op_name
if variables:
body["variables"] = variables
if input_data:
if variables in body:
body["variables"]["input"] = input_data
else:
body["variables"] = {"input": input_data}
if headers:
resp = self._client.post(
self.GRAPHQL_URL,
json.dumps(body),
content_type="application/json",
**headers
)
else:
resp = self._client.post(
self.GRAPHQL_URL, json.dumps(body), content_type="application/json"
)
return resp
return graphql_query(
query,
op_name=op_name,
input_data=input_data,
variables=variables,
headers=headers,
client=self._client,
graphql_url=self.GRAPHQL_URL,
)

def assertResponseNoErrors(self, resp):
"""
Expand Down