• Unit Testing django-graphene GraphQL endpoints with pytest

    On a project at work I've been learning GraphQL. I'm in charge of both developing the backend ( using the wonderful graphene-django package) and the frontend ( using Typescript / Vue.js / axios ) for this specific feature.

    After wrapping my head around GraphQL and getting a basic endpoint working I wanted to write some tests to ensure that filtering and other requirements are working as expected.

    For a proper End-to-end test I'd usually setup a client and post a json dictionary or such. With End-to-end GraphQL tests you need to send actual GraphQL queries, which makes sense but it feels a bit like SQL Injection.

    Below is how I've settled organizing and writing my GraphQL tests.

    tests/
    β”œβ”€β”€ __init__.py
    β”œβ”€β”€ conftest.py
    β”œβ”€β”€ test_myapp/
    β”‚Β Β  └── test_schema.py

    Because the graphql client will be reused across the django site, I added a global fixture that will automatically load mty project's schema.

    # tests/conftest.py
    @pytest.fixture
    def graphql_client():
    from myproject.schema import schema
    from graphene.test import Client
    return Client(schema)

    In this example I'm testing that I'm filtering data as expected when passing a search parameter.

    For setup, first I write a query as its own fixture so I can re-use it throughout the test and it's clear exactly what is going to be run. Second, I make sure the query uses variables instead of hard-coded values when querying so I can change the input depending on the test. Third, setup a model_bakery fixture for data that I expect to find.

    import pytest
    from model_bakery import baker

    @pytest.mark.django_db
    class TestMyModelData:
    @pytest.fixture
    def query(self):
    return """
    query testGetMyModel($searchParam: String!){
    myModelData(searchParam: $searchParam) {
    totalCount
    }
    }"""

    @pytest.fixture
    def my_model(self):
    baker.make(
    "myapp.MyModel",
    total_count="20", # Decimal field
    )

    def test_none_response(self, graphql_client, query, my_model):
    executed = graphql_client.execute(query, variables={"searchParam": "skittles"})
    assert executed == {"data": {"myModelData": None}}

    def test_filters_usage(self, graphql_client, query, my_model):
    params = {"searchParam": "skittles"}
    executed = graphql_client.execute(query, variables=params)
    assert executed == {
    "data": {
    "myModelData": {
    "totalCount": 20
    }
    }
    }

    Executing each test I simply pass my query and required variables for the test/query. In this I'm testing the same query twice: once with and one without a searchParameter. My expectation is that I get no results without a search term and data when to my graphql_client fixture.

    As the return value from our client is a dictionary, I can simply assert my expecte results with the actual results. If something changes I'll know immediately.

    Using the techniques above I can easily add new tests for my GraphQL endpoint as the available changes or bugs are found.

  • Fuji looking grand tonight. Autumn Fuji is the best.

  • Checkin to KUA`AINA

    in Yokohama, Kanagawa, Japan
    Avocado Cheddar burger!
  • Checked in at Kua 'Aina (KUA`AINA). Avocado Cheddar burger!

  • Did a presentation at work today about editing PDFs with Python. Mixing and matching libraries is fast, but you’ll run into a bunch of inefficiencies that result in slow processing and huge files. I should write a book. For serious.

Previous 390 of 725 Next