Testing GraphQL Subscriptions

Javier Fernandes
4 min readSep 5, 2017

--

GraphQL subscriptions are a relatively new “feature”. Although there are already a couple of tutorials on how to use it (i.e. https://dev-blog.apollodata.com/graphql-subscriptions-in-apollo-client-9a2457f015fb, https://blog.graph.cool/how-to-build-a-real-time-chat-with-graphql-subscriptions-and-apollo-d4004369b0d4, etc), I was not able to find any of explaining how to “test” them.

Seems like no body does TDD anymore these days :P

So this brief post is like a snippet on how to test a subscription using mocha/chai, like in an “integration-test”

What we are going to test is the Server part of the subscriptions, and not the client (browser).

The API

In this case the Graph API has

  • an Object type: which are like generic entities of the system. Each object belongs to a “project”, which is another concept of the domain. They are stored in a mongo database through mongoose.
  • an updateObject(project, objectId, property, value) mutation: used to update a property on one of those objects.
  • an objectUpdated(project:String) subscription: used to subscribe to events on any object of the given project.

This means that when a client performs am “updateObject” mutation the server will trigger an event for subscriptions on that same project.

The resolvers code is not important at this point nor any other Query

Testing: first test, mutation

First lets do a test for the mutation alone. This is the final test with some “magic”

The magic here is in `expectQueryResult()` which already encapsulates a lot of complexity under the form of a function similar to an “equals” assert.

The signature is

 expectQueryResult( query, expectedResponse)

Now this function gets “injected” as part of the body of the test, if you look at the first line, because we are not using the regular “describe()” method but a “describeGQL()` function

describeGQL("some text", ({ expectQueryResult }) => {
// tests here using expectQueryResult
})

describeGQL hides away a lot of setup to reuse between different tests

describeGQL(): test “infrastructure”

This function follows kind of a “pattern” that I follow when I found myself doing the same code in many different “tests” in mocha. For example all express routes tests, or all tests that require mongo, etc. They usually have some before(), after(), beforeEach(), afterEach() plus all the required imports.

The way to solve this is to create like a “describe” replacement or function that wraps an original describe, and can be used as a regular describe (meaning **describe.skip** and **describe.only**)

So it uses high-order function for that

This has some specifics from the project like where the mongoose models are, or how to build the schema. If you follow any other graphql tutorial you will end up with the schema, that is the only thing that you need here.

Notice that the wrapper ends up calling the “body” function, that is your code within the describe() block, sending an object with some parameters.

This object has basically all the utility methods to do graphql expectations/asserts. As they require the connection and context they cannot be just functions out there.

So this is how the test receives the **expectQueryResult()** function :)

Finally: Testing Subscription

So to test subscriptions we need to change the **describeGQL()** function because it needs some setup:

  • Start the backend subscriptions server (websockets)
  • Create a graphql-subscription client connecting to the server (we will use apollo-client here on the server, just for test)
  • Provide a way for the test to subscribe

First install some libraries for this

yarn install apollo-client graphql-tag ws

Now lets change describeGQL() by adding some imports and constants

Then in the before() start the server and create an Apollo client

And we need to shut it down on the after

  after(done => {
networkInterface.close() // stop client
rtm.shutdown() // << stop server

// ... some others
})

And finally we will make “apollo” available for the test body function.

const client = () => apollo// interpret bodybody({ expectQuery, expectQueryResult, execGraphQL, client }, schema)

So now the we can add a test

In the first part we use the “client” object to subscribe to a subscription query. That gives us an Observable for which we register an observer that just adapts it to a promise. When it receives data, it resolves the promise. If there is an error, then rejects it.

Then we call a mutation to simulate someone changing and object.

Finally we wait for the subscription promise and assert that it receives the correct objects.

And that’s it.

Or not.. this code can be refactor. I don’t like the subscription being a promise. To make it scale it would be good to have some kind of generic “subscriptor” or listener to which one can ask for all the events that it received. Kind of redux mockstore.getActions().

But this is enough for today :)

I hope this helped a little bit since I couldn’t find any clear documentation on how to do this tests.

Annex

Here is the complete describeGQL() function

--

--

Javier Fernandes

Software Engineer@SCVSoft. Professor@UNQ (Public National University, Argentina). Creator of Wollok Programming Language. Always learning & creating things :)