DEV Community

Cover image for Test GraphQL applications with graphql-testx
Davide Bizzarri for AeroGear

Posted on

Test GraphQL applications with graphql-testx

Testing is an essential part of application development. Building an application without tests can lead to many problems in future maintenance.

Tests can be classified into multiple categories. Developers usually divide tests between unit tests and integration tests. Unit tests are the easiest to write and maintain because they are stable and self-contained. Our application can rely on external services therefore we will also have to write integration tests.

Alt Text

Integration tests require a separate environment that can be difficult to set up. When developing a new feature that requires changes to both the client and server, we will need to wait for the server to be available and running before we can start writing the integration tests.

Mocking the server

Because of the difficulty of writing integration tests, you may look into a way to mock the server in order to test the application without mocking its functionality.

Mocking is the practice of creating a fake version of a component so that you can develop and test other parts of your application independently. Mocking your backend is a great way to quickly build a prototype of your frontend and lets you test your frontend without starting up any servers. — Jonas Helfer, Mocking your server with just one line of code

Mocking offers a powerful and flexible way to simulate the server, but it comes with the compromise that query results will be auto-generated by default, and mutations will return one to one or auto-generated data. This doesn't provide you with a realistic way to test the application when it relies on data consistency for some of the internals logics, like caching or conflict resolution.

graphql-testx to the rescue

graphql-testx approaches the problem differently, instead of auto-generating the results, it will generate the CRUD resolvers for each type. All resolvers will read and write data to a database providing data consistency between write and read operations. A practical example of the problem:

const { TestxServer } = require("graphql-testx");
const { request } = require("graphql-request");

(async () => {
 let result;

 // We define our server using a datamodel definition
 const server = new TestxServer(`
   type Foo {
     id: ID!
     bar: String!
   }`);

 await server.start();

 // When the TestxServer start it will create and expose
 // the graphql server to an available random port on localhost,
 // therefore we need to ask the TestxServer the address of the
 // graphql server
 const serverUrl = await server.httUrl();

 // TestxServer will also generate ready to use queries and mutations
 const CREATE_FOO = (await server.getMutations()).createFoo;
 const FIND_ALL_FOOS = (await server.getQueries()).findAllFoos;

 // The create mutation will store the data in the database
 // generate an id for us
 result = await request(serverUrl, CREATE_FOO, { bar: "hello" });
 console.log(result);
 // { createFoo: { id: '1', bar: 'hello' } }

 // Because our data are stored in a database, subsequent queries
 // will return the same data
 result = await request(serverUrl, FIND_ALL_FOOS);
 console.log(result);
 // { findAllFoos: [ { id: '1', bar: 'hello' } ] }
})();

graphql-testx is using Graphback under the hood to parse the datamodel and generate the GraphQL schema, database, resolvers, client queries, and mutations. This means that the same datamodel definitions rules of Graphback apply also to graphql-testx.

You can print out the generated GraphQL schema and database schema using the server.getGraphqlSchema() and server.getDatabaseSchema()

Alt Text

An offline journey

We started working on graphql-testx to write a scalable and robust integration tests layer for Offix. Offix is a library based on Apollo Graphql that helps developers create offline applications.

From the testing viewpoint, we need to perform one or more mutations while the device or browser is offline, then we put the device back online to send all mutations, and lastly we query the server to ensure that all mutations have been applied correctly.

We initially developed a small GraphQL server with basic queries and mutations that reads and writes into javascript objects, but soon we started to struggling in testing more complex queries and mutations with multiple relationships between types and we were also investing a considerable amount of time in maintaining it.

So we developed graphql-testx to rewrite all tests in fewer lines and
start focusing on adding more complex tests for covering more use cases.
To learn more on how we did it you can try out our Offix example
in the graphql-testx repo.

Alt Text

Discussion (0)