Test GraphQL Apis Easily and Asynchronously

I believe that one of the things that questions you the most is how we could test our GraphQL Apis. There are several ways to do them and there are several approaches that can be taken.

I like to have a simple and intuitive approach in the best possible way. And that it can still be reusable.

I'll give you a brief introduction to today's example. I made an Api using Fastify and mercurius (formerly fastify-gql), this because it already has support for async middleware support and is quite simple to configure. In addition, axios is used to make http requests to the jsonplaceholder Api to get a user and posts from that same user.

As you may have already understood, we use the axios only to consume the data from the external Rest Api while we are going to consume our Api using GraphQL.

Today we are going to use uvu, which is a super fast and extremely light test runner, but more importantly, it already supports asynchronous testing out of the box without any configuration.

And to do the GraphQL tests I'm going to use a super easy-to-use client called graphql-request, which also supports asynchronous requests.

At the end of the article I will share the repository link so you can clone the example from this article and so you can test it yourself.

Let's code

Imagine that your Api is as follows:

// @src/server.js

const Fastify = require("fastify");
const mercurius = require("mercurius");
const { gql } = require("graphql-request");
const axios = require("axios");

const app = Fastify();

const schema = gql`
  type Company {
    name: String
    catchPhrase: String
    bs: String
  }
  type Geo {
    lat: String
    lng: String
  }
  type Address {
    street: String
    suite: String
    city: String
    zipcode: String
    geo: Geo
  }
  type User {
    id: ID
    name: String
    username: String
    email: String
    phone: String
    website: String
    company: Company
    address: Address
  }
  type Find {
    userId: ID
    id: ID
    title: String
    body: String
    user: User
  }
  type Query {
    find(id: ID): Find
  }
`;

const resolvers = {
  Query: {
    find: async (root, { id }, ctx) => {
      const getUser = axios.get(`https://jsonplaceholder.typicode.com/users/${id}`);
      const getPost = axios.get(`https://jsonplaceholder.typicode.com/posts?userId=${id}`);
      const promises = await axios.all([getUser, getPost]);
      const user = promises[0].data;
      const post = promises[1].data[0];
      return { ...post, user };
    },
  },
};

app.register(mercurius, {
  schema,
  resolvers,
});

async function start(port) {
  try {
    await app.listen(port, () => {
      console.log(`Api running at http://localhost:${port}/graphql`);
    });
  } catch (err) {
    console.error(err);
    process.exit();
  }
}
start(3333);

As you can see, we only have one resolver, which is a Query that can be performed. And if you look at the schema variable, you can get an idea of the data we can choose when doing our query. Now that we have these points in mind, let's get to work on our tests.

Unlike many test libraries/frameworks, uvu does not group a set of tests, each test is always done individually. For beginners, it's an ideal environment.

First let's import uvu and the small assertion library which is composed of a set of methods.

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");

test("Should get the post and user data through the given id", async () => {
  // Logic goes here
});

test.run();

Now we can import the graphql-request, so we can make the request and do our query.

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");
const { request, gql } = require("graphql-request");

test("Should get the post and user data through the given id", async () => {
  // Logic goes here
});

test.run();

If you read our Api code carefully, our Query receives only one parameter which is the ID. In this test I will get the user data with ID 1 and then we will get the following data:

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");
const { request, gql } = require("graphql-request");

test("Should get the post and user data through the given id", async () => {
  const query = gql`
    query {
      find(id: 1) {
        title
        body
        user {
          username
          email
          address {
            street
          }
          company {
            name
          }
        }
      }
    }
  `;
  // More logic goes here
});

test.run();

Now we just have to make the request, first we define the URL and then we pass our query.

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");
const { request, gql } = require("graphql-request");

test("Should get the post and user data through the given id", async () => {
  const query = gql`
    query {
      find(id: 1) {
        title
        body
        user {
          username
          email
          address {
            street
          }
          company {
            name
          }
        }
      }
    }
  `;
  const { find: data } = await request("http://localhost:3333/graphql", query);
  // More logic goes here
});

test.run();

Now we have to check the data, according to the schema, we know that the root of the answer is an instance of an object, as well as the user data, among others.

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");
const { request, gql } = require("graphql-request");

test("Should get the post and user data through the given id", async () => {
  const query = gql`
    query {
      find(id: 1) {
        title
        body
        user {
          username
          email
          address {
            street
          }
          company {
            name
          }
        }
      }
    }
  `;
  const { find: data } = await request("http://localhost:3333/graphql", query);
  assert.instance(data, Object);
  assert.instance(data.user, Object);
  assert.instance(data.user.address, Object);
  // Even more logic goes here
});

test.run();

As we got the user data with ID 1, we know the username must be Bret and he must live on Kulas Light street. So we have to check if the response data matches what is expected.

// @src/tests/api.test.js

const { test } = require("uvu");
const assert = require("uvu/assert");
const { request, gql } = require("graphql-request");

test("Should get the post and user data through the given id", async () => {
  const query = gql`
    query {
      find(id: 1) {
        title
        body
        user {
          username
          email
          address {
            street
          }
          company {
            name
          }
        }
      }
    }
  `;
  const { find: data } = await request("http://localhost:3333/graphql", query);
  assert.instance(data, Object);
  assert.instance(data.user, Object);
  assert.instance(data.user.address, Object);
  assert.equal(data.user.username, "Bret");
  assert.equal(data.user.address.street, "Kulas Light");
});

test.run();

Now we just run the npm test command to run the tests and see if it passes. But first you have to run the Api on port 3333 using the npm start command and only then run the tests. You should get a result similar to the following:

The github repository is here.

Conclusion

Although the example is simple I hope it has helped you and that you enjoy testing with this behavioral testing approach.

Have a nice day! 👋

17