How to Test your GraphQL Endpoints

Testing your GraphQL API is critical to ensure that your software's business logic is running as expected. Tests will reveal bugs and vulnerabilities before they make it to production. In this tutorial, we go through the practical steps to write unit and integration tests for your GraphQL API.

How to Test your GraphQL Endpoints

GraphQL is becoming more and more popular to build amazing products with a modern stack, and that makes total sense (that’s the path we took at Escape as well 😎)

But as with any new technology, it comes with the downside of not finding a lot of good resources.

Today, we address a crucial point: testing your GraphQL endpoints!

Why you should write tests for GraphQL APIs?

Testing your API is important to make sure that the business logic of your software is running properly.

Writing tests will allow you to reveal bugs before they make it to production - which we absolutely want to avoid (especially if you deploy on Fridays 😬).

With tests, you can verify functionalities, reliability, performance, security, and all sorts of potential issues.

Finally, keeping a good testing coverage will ensure that future updates of your API are not breaking the front-end applications consuming it.

How is GraphQL different from REST APIs?

The primary difference is that REST APIs have several endpoints to test, but in GraphQL you only have one (in most cases). Therefore, the structure of your tests will be different from the usual REST APIs.

Another important difference is that GraphQL is a strongly-typed language that makes it extremely convenient to detect bugs as you code, especially when working on a massive codebase.

These differences make testing REST and GraphQL APIs fundamentally different. Now let’s see how it actually works!

Writing tests for GraphQL APIs

Follow our GraphQL example

Let’s say we’re building the next Facebook. Our graph of entities is going to look something like this:

Our app will need features like sending a friend request which will require a mutation and displaying a feed based on friends’ posts - which will require a query (get recent posts from friends).

As we said previously, there are several layers we need to test in a GraphQL API:

  • schema - testing your type definitions
  • queries and mutations - testing the definition of your queries and mutations, isolated from the actual execution logic
  • resolvers - testing the last bit, the Javascript functions that resolve the queries and mutations.

Copying and installing the base repository

If you want to follow along, you can clone our repository and follow the instructions from the README file to install and run the project.

Testing schema

GraphQL is a strongly typed language, which makes it very handy to detect errors directly from your IDE.

To validate our schema, we can use two libraries:

  • eslint-plugin-graphql: a linter to extend our Eslint rules
  • graphql-schema-linter: a CLI tool to validate our schema definition
npm install --save-dev eslint-plugin-graphql graphql-schema-linter

Testing GraphQL queries and mutations

You can start with basic, manual testing on GraphiQL. But at some point, automated tests are a must.

The best way to isolate your queries and mutations is to use EasyGraphQL Tester, a library to run your GraphQL code without firing up a server.

npm install --save-dev easygraphql-tester

We are not yet looking at how we are going to execute these queries and mutations to get or modify data, just their definition:

  • name of the query or mutation
  • their arguments (name and type)
  • the returned type

Here’s what it looks like:

describe('Queries', () => {
			let tester
			beforeAll(() => {
			    tester = new EasyGraphQLTester(schemaCode)
			})
			
			test('Should get friends with a nested query', () => {
	        const query = `
	            query GET_FRIENDS($userId: ID!){
	                user(id: $userId) {
	                    id
	                    friends {
	                        id
	                        firstname
	                        lastname
	                    }
	                }
	            }
	        `
	        tester.test(true, query, { userId: '0' })
	    })
})

Testing resolvers

Resolvers are simply Javascript functions that we can test like any other Javascript code. Let’s go through an example with our resolver that gets the friends of a user:

it('Should get friends of user from their id', () => {
      const userId = 0
      const friends = getFriends(db, userId)
      expect(friends.length).toBe(1)
      expect(friends[0]).toHaveProperty('id')
      expect(friends[0]).toHaveProperty('firstname')
      expect(friends[0].firstname).toBe('Eduardo')
})

That’s all there is to it!

Integration tests

Now we can put it all together with an integration test to make sure that all the components - schema, the query, and the resolver - are working properly:

it('Should return friends of the user with given id', async () => {
		const query = `
		    query GET_FRIENDS($userId: ID!) {
		        user(id: $userId) {
		            id
		            friends {
		                id
		            }
		        }
		    }
		`
		const args = { userId: '0' }
		const result = await tester.graphql(query, undefined, { db }, args)
		
		expect(result.data.user.id).toBe(args.userId)
		expect(result.data.user.friends).toBeInstanceOf(Array)
		expect(result.data.user.friends).toContainEqual({ id: '1' })
})

Automated GraphQL testing

Performance, reliability, and security are requirements to ship production-ready applications. But GraphQL just lacked the proper tooling…

This is why we created Escape, the first GraphQL automated scanner that allows developers to find and fix bugs in GraphQL applications in CI/CD during the development lifecycle before they even reach production!

Want to get tips on how to secure GraphQL and see Escape in action for your GraphQL APIs? Book a demo with us 👇

It understands the business logic of your GraphQL API and looks for more than 50 kinds of vulnerabilities so that you never have to worry again about

  • Resolver performance (N+1 issues, cyclic queries, query complexity DOS…)
  • Tenant isolation (access control, data segregation between users…)
  • Sensitive data leaks (personally identifiable information, tokens, secrets…)
  • Injections (SQL, NoSQL, XSS…) and requests forgery
  • Proper error handling (server errors, stack-traces…)
  • Compliance and reporting (PCI-DSS, GPDR, HIPAA, OWASP TOP 10…)
  • and 50+ more in-depth tests…

We constantly update our engine with state-of-the-art technology so that you never have to worry about the security, performance, and reliability of your GraphQL application again!

👉 ️Find the bugs in your GraphQL API for free in minutes

BONUS: testing the HTTP server

We’ve seen how to test the GraphQL part of our API, but we left off an important aspect of it: the HTTP server.

The frontend applications will consume our API through HTTP requests, and it wouldn’t be a proper API testing tutorial without covering that part as well.

This last part includes:

  • testing the request headers and body
  • testing the response status and payload

This time, we need a running server. We can user Supertest a library that does just that

npm install --save-dev supertest

Now we can use it to run our server and send HTTP requests in our tests:

const supertest = require('supertest')
const app = require('./app')

app.server = app.listen(5000)

describe('Server', () => {
    afterEach(() => app.server.close())

    it('Should listen to HTTP requests', (done) => {
        const userId = '0'
        request(app)
            .post('/graphql')
            .send({
                query: `{ user(id: ${userId}) { id, friends{ id } }  }`,
            })
            .set('Accept', 'application/json')
            .expect(200)
            .end((err, res) => {
                if (err) return done(err)
                expect(res.body && typeof res.body === 'object').toBe(true)
                expect(res.body).toHaveProperty('data')
                expect(res.body.data).toHaveProperty('user')
                expect(res.body.data.user).toHaveProperty('id')
                expect(res.body.data.user.id).toBe(userId)

                expect(res.body.data.user).toHaveProperty('friends')
                expect(res.body.data.user.friends).toContainEqual({ id: '1' })
                return done()
            })
    })
})

Running tests on your GraphQL inside GitHub actions

Finally, a good practice is to run tests with a Github Action so that you only deploy your changes when they pass all the tests ✅

name: Run tests
on: [push]
jobs:
    tests:
        name: Run tests
        runs-on: ubuntu-latest
        steps:
            - name: Checkout
              uses: actions/checkout@v2

            - name: Install dependencies
              run: npm install
						- name: Schema linter
							run: npm run graphql:lint

            - name: Run tests
              run: npm run test

Conclusion

Let’s recap. We saw why not testing your app is not an option and how you can test your GraphQL endpoint:

  1. Test your schema with eslint-plugin-graphql and schema-graphql-linter
  2. Test your queries and mutations with EasyGraphQL Tester
  3. Test your resolvers like you would test any other Javascript function

💡Interested in learning more GraphQL APIs testing? Check out the articles below: