Testing GraphQL for the Beginner Pythonistas

Testing your GraphQL application

Introduction

Something that is untested is broken.

For the developer, it can uncover bugs in the code very early. For the client it will increase the amount of trust on you (and preference). In this article we focus on the GraphQL testing on your application. In other words we will automate what you would do manually to ensure proper functionality of the GraphQL endpoint. E.g. with Postman or curl. Let’s start.

Scope

Code

Testing the Graphene way

Querying

But what are we going to test anyway? Of course, to keep this artcile short we will not test everything. But the idea is to initialize the database of the application, and then run our query for getting all the posts.

The json response that you will receive if you test with the GraphQL client included with Graphene is

In other words our first step is to test that the initial state of our application is what we expect it to be. Only one post with the above content. Using naively the textbook approach in the following fragment

will fail spectacularly. In order to test it you can use the CLI, go to the top folder of the project and issue

The error is summarized in the traceback clearly if you search

This typically means that you attempted to use functionality that needed
to interface with the current application object in some way. To solve
this, set up an application context with app.app_context(). See the
documentation for more information.

It means that everything runs as if Flask does not exist, but behind the scenes, Flask is used heavily. In order to solve it we need to create and use a fixture that during its lifetime it sets up a Flask app context. The bright idea is to use a context manager to be our portal to Flask-land. While our test, the context is up and we ensure it by requiring the fixture. Given the context we can safely execute our test. The context creation is taken for the official Flask testing tutorial which is a treasure trove.

Mutating

The query under question is :

You can see that we set through JSON a set of variables to parametrize the mutation query in Postman in the right panel. In the bottom we can see the result. Consequently the full test takes the form.

Testing the Behave way

REST to the rescue

We will use Postman this time as a classical REST client. For the allposts query above the serialization takes the form

and the Postman request (we have added “Content-Type” : “application/json” in headers)

For the post creation, the serialization is done as follows (note the query !!!)

with Postman request

Armed with this knowledge, lets write our Behave tests. This is a very basic introduction and the reader is expected to consult the excellent tutorial of Behave. We first take the role of the customer of the consumer of the api and describe in the Gerkin language our expectations.

In prose, we initialize our database with init-db as previously, we create a post with specific data and finally a new post exists only with data. The limitation, is as the test describes literary, every time we have to initialize the database. We cannot reuse a running instance , because the databased is not initialized and “only one post” is violated. We will see later how this is tackled. For now we will “suffer” temporarily.

This file is created in a features sub-folder where we also create a steps sub-folder to host our python implementation of the statements. In the steps sub-folder create a posting.py skeleton populated with the steps (dummy implementations):

Lets focus on the first one. We have our database initialised and the application running (see documentation in source) and we make sure that the state of the application, for the posts perspective is as it should. In other words it contains only one post with title “Flowers” for user 1. We can make it as detailed as we need. For demo purposes, it suffices. We make a REST call to the running Flask application and assert on the return value. The idea is taken from here.

In this case, we serialized the GraphQL query to a json payload, we setup the headers to signify a JSON payload, we do a POST, and finally we make sure that the response is what we expected. Nothing special. The rest of the steps are implemented this way. How we run the tests? We open a terminal to our folder containing the source and start the application

In an other terminal, go to the top folder and run behave to unleash the magic. In order to re-run the test we must kill and run the shell script again. This is a shortcoming. We will fix it now.

Starting stopping the Flask application programmatically

The plan is to start the server in the before_feature section and shut it down in the after_feature section, which means before and after the file containing the scenarios are accessed. I had failed many times to make it work, but every time I learned something. The material comes from this posting for the threaded trick. Please read various documentations for context push and threads to understand what is happening. But the principle is textbook level, start a thread running the server in order not to block test execution (before_feature) and then signal the thread to stop (after_feature). Lets code it assumming we have an object that represents the server itself.

The implementation details can be looked up in the code. Given this definition, we can now describe the server managment process before/after feature execution.

We will need the excellent threading package. We manage server and execution thread independently here. We can do both, but this is basic introduction.

Here we have the typical function that is threaded and where we execute the server. Before the feature is run we create a server, a thread that runs the server and we start the server. In order to cleanup, we stop the server and make sure that thread has finished properly. With a correct implemntation of the MyServer where we initialise the database, we can run the test as many times as we want without any manual intervention.

Conclusion