diff --git a/docs/testing.rst b/docs/testing.rst index 473a9ba1b..23acef2d4 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -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/"`. @@ -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( ''' @@ -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 \ No newline at end of file diff --git a/graphene_django/tests/test_utils.py b/graphene_django/tests/test_utils.py index c0d376b6f..f5a8b0576 100644 --- a/graphene_django/tests/test_utils.py +++ b/graphene_django/tests/test_utils.py @@ -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(): @@ -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"}} diff --git a/graphene_django/utils/testing.py b/graphene_django/utils/testing.py index 0f68a512f..6b2d3e8c3 100644 --- a/graphene_django/utils/testing.py +++ b/graphene_django/utils/testing.py @@ -2,6 +2,63 @@ 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): """ @@ -9,19 +66,12 @@ class GraphQLTestCase(TestCase): """ # 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): @@ -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): """