In the previous chapter, we reviewed some of the basics of GraphQL, differences between REST and GraphQL and core concepts of GraphQL. Now let’s understand more about some of the internal working of core-concepts. And, What components suggested for testing GraphQL?
Table of Contents: The Definitive Guide to Testing GraphQL API
- Introduction and getting started with GraphQL
- You’re here → Testing GraphQL APIs
- Cross-functional testing of GraphQL APIs – Coming Soon
GraphQL Test Playground
We will be working with a simple Movie and Actor based GraphQL API that shows some name of the movie and list of movies and actor details. I’ve developed and Open-Sourced here. Firstly, let us look at the Schema of the API that we are going to use for testing. And we will be using the same for the upcoming chapters in this course.
I’ve used code sandbox to deploy the source code of GraphQL server component and it is accessible here. This will be available for access for a limited time, and when you are reading this and the sandbox environment is not accessible, I will request you to clone the source code and build it locally.
To recap, we’ve learned from our previous chapter that, everything is perceived as a graph and it’s connected (think of the GraphQL Logo) and represented as a single endpoint. In REST, we can see multiple endpoints for different functions of your product. In GraphQL, it’s through a single endpoint, and the main entry point is via RootQueryType
as the name suggests, it’s the root and a single entry point towards accessing your server component. The below picture depicts precisely that, and we have four types movie
, actor
, movies
and actors
that users can query for data. And we’ve also seen different scenarios that users may query in the previous chapter.
Querying GraphQL API
Before we jump onto testing components of GraphQL, firstly we need to understand how to call a GraphQL API over HTTP. There are different ways in which you could trigger an API. I’ve shared below some easy and commonly used ways as below:
-
- cURL: Curl is a popular command-line tool and library for processing data over HTTP (and many other) protocols. You will have to send a curl command along with three parameters.
-
-
- Firstly, the content-type
application/json
because the query is part of JSON. - Secondly, the data sent will be actual query like
{ "query": "{ movies { name } }" }
- Finally, the GraphQL endpoint:
https://n7b67.sse.codesandbox.io/graphql?
And mostly all the GraphQL calls would be based on HTTP verb POST. - In summary, below is the curl command to query your GraphQL endpoint.
- Firstly, the content-type
-
-
- cURL: Curl is a popular command-line tool and library for processing data over HTTP (and many other) protocols. You will have to send a curl command along with three parameters.
curl -X POST -H "Content-Type: application/json" --data '{ "query": "{ movies { name, genre } }" }' https://n7b67.sse.codesandbox.io/graphql | jq .
- GraphiQL: It’s a React-based Front-end that provides a nice GUI for us to compose queries from the browser. Users have an option to set a boolean flag to set
graphiql
to beTrue
in yourapp.js
file. Then, GraphiQL is accessible automatically the moment you build the server component vianodemon
ornode
.
- Using
fetch
library: The code below is written based on JavaScript. But you can do this in any language. It is basically an extension of the cURL command that we saw above.require('isomorphic-fetch'); fetch('https://n7b67.sse.codesandbox.io/graphql', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: '{ movies { name, genre } }' }), }) .then(response => response.json()) .then(response => console.log(response.data));
Testing GraphQL
There are several ways your application could break, and testing is very much an essential activity to release code with confidence. Speaking of testing GraphQL API’s, there are multiple ways and methodologies that you could perform. Firstly, let us take a look at the components to check in GraphQL and then we will look at implementing a testing methodology.
We all know the famous Test Pyramid by Mike Cohn. It’s a great visual metaphor telling about the tests you could write at different levels. We all know it is proven to be effective in many ways for different environments and scenarios.
There is also a new way of looking at the organizing of your tests called the Testing Trophy 🏆 that helps write tests at different granular levels. I like both the testing metaphors, and it’s best to apply whatever works per the environment and the scenario. Testing GraphQL APIs can utilize the Testing Trophy very well because the lower level of the testing trophy recommends checking for static checks. We have a Schema defined with its strong types and stands as a source of truth to other field type and parameters, you can easily do a lot of static checks. Let’s look at some of the core components in GraphQL that should be tested which will fit into different levels like the Unit, Integration and Static checks.
Components to test in GraphQL
The first choice to go about testing GraphQL APIs would be to look at Schema and check for those types that you’d have had defined. Here is a cool handy cheat-sheet for reference to write schema succinctly, useful for developers and testers. Schema can be tested by the following ways:
- Static Type Checks: A lot of these can be tested using two of my favourite tools, the graphql-schema-linter and eslint-plugin-graphql and of course you could use Flow to perform static checks.
- Mock Schema & Test queries: You should consider mocking the schema using
mock-server
that should be available in bothgraphql-tools
orapollo-server
tools and write tests(queries) with all possible combinations.
import { makeExecutableSchema } from '@graphql-tools/schema'; import { addMocksToSchema } from '@graphql-tools/mock'; import { graphql } from 'graphql'; const schemaString = `type Query { movie(id: ID): Movie actor(id: ID): Actor } type Actor { id:String! name:String! age:Int movies:Movie } type Movie { id:String name:String! genre:String actor:Actor } type Movies { movies:[Movie] } type Actors { actor:[Actors] }`; // Make a GraphQL schema with no resolvers const schema = makeExecutableSchema({ typeDefs: schemaString }); // Create a new schema with mocks const schemaWithMocks = addMocksToSchema({ schema }); const query = ` query getmoviewithid { movie(id: 6) { name, genre } } `; graphql(schemaWithMocks, query).then((result) => console.log('Got result', result));
Testing queries are easy with GraphiQL
if you are using JavaScript to build GraphQL server components. There are also other plugins to like Altair that can be used to test if you are using other language clients to build GraphQL server. The above GIF under GraphiQL section exactly shows the same.
- You can also test the queries in an automated fashion using libraries like
request
andsupertest
const supertest = require("supertest"); const expect = require("chai").expect; const schema = require("../schema/schema"); let baseURL = supertest("http://localhost:4000/graphql"); let list_users = `{ movie(id:"1234595830") { name id actor { name age id } } } `; describe("POST Request", async () => { let post_resp; it("makes a POST call ", async () => { post_resp = await baseURL .post(list_users); await console.log(post_resp.body); //Do any other validation here. }); });
- Using
easygraphql-tester
you could test for different queries against the schema definition.const EasyGraphQLTester = require('easygraphql-tester') const fs = require('fs') const path = require('path') const schemaCode = fs.readFileSync( path.join("./schema/movies-schema.gql"), "utf8" ); const tester = new EasyGraphQLTester(schemaCode) describe("Test Schema, Queries and Mutation", () => { let tester; before(() => { tester = new EasyGraphQLTester(schemaCode); //just to make sure schema comes through swiftly //console.log(util.inspect(tester)) }); it("Should pass with a valid query", () => { const query = ` { movies { name genre actor{ name age } } } `; // First arg: true because the query is valid // Second arg: query to test tester.test(true, query); }); });
Mutations modify data in our database and return us a value. We’ve learned from the previous chapter that Mutations are equivalent to CRUD operations in the REST API world. Testing Mutations are very important as it involves testing data access and addition to databases. Mutations can be tested by the following ways:
- Using GraphiQL: Use the below mutation query in the GraphiQL client and test for valid response and message.
mutation{ addMovie(name: "MovieName", genre: "Action", actorId:"The ID of Actor"){ name genre } }
- Using
easygraphql-tester
it("Should fail with a Invalid mutation query", () => { const mutation = ` mutation addMovie($id: String!, $name: String!, $genre: String) { addMovie(id: $id, name: $name, genre: $genre) { id name genre } } `; // First arg: false because the query is Invalid // Second arg: query to test tester.test(true, mutation, { id: "id123", name: "testMovie", genre: "Action", }); });
Resolvers are query handlers and are pure JavaScript functions. The Resolver function would typically have the following syntax:
fieldName(obj, args, context, info) { result }
Let’s take our example query to get movies as below and see how Resolver functions would be like:
query { movies{ name genre actor{ name } } }
obj
inQuery.movies
will be whatever the server configuration passed forrootValue
.obj
inMovie.name
andMovie.genre
will be the result frommovies
, usually, a movies object from the backend.obj
inActor.name
and will be one item from theactor
result array.
GraphQL query is a series of function calls (resolver functions) nested in a way according to the query pattern. So testing Resolvers would be the same as unit testing a function in JavaScript.
I hope that by now, you are aware of the core component of GraphQL and how to test those components. Now let’s go ahead and look at how to test GraphQL APIs using some of the tools and libraries in the API testing ecosystem.
Testing GraphQL using Tools in the API Ecosystem
Testing GraphQL with Postman
Exploratory testing with GraphQL is quite simple, Postman has inbuilt support to run GraphQL queries. Let’s see how can you do that:
- Step 1: Select the Schema type as GraphQL under New API dialog. Upload your GraphQL Schema onto your Postman, this will help us assist writing easy queries and also help us with supporting query completion. This is optional, and you could even test it without uploading Schema.
- Step 2: Next up, you can start creating a collection and add an API call onto your collection. In this case, I’ve named our collection as
TestProject-GraphQL
and have threePOST
calls, as shown on the left-hand side in the below picture. I also have three differentPOST
calls testing for different data.
- Step 3: Next, the API test is just to demonstrate the usage of Query variable, say when you’d want to query for a particular
Movie
orActor
using its unique identifier.query($id: ID!){ actor(id:$id){ name age movies{ name } } }
- Step 4: We can make use of
tests
tab in Postman to automate and validate the response.pm.test("Status code is 200", function () { pm.response.to.have.status(200); }); pm.test("Verify Movie Name", function () { var responsePayload = pm.response.json(); pm.expect(responsePayload.data.movies).to.be.oneOf(["The Matrix","Inception","Inferno"]); });
- Step 5: We can automate these tests using
Tests
section and use Postman Runner to execute your GraphQL API tests via command line too.
🔱 Myth: GraphQL always returns 200 as status code.
It’s advisable not to check for status code, as you see on the above screenshot the
Get List Of All Movies
test has failed but still shows 200 as status code. GraphQL allows you to configure custom error codes, so the best practice would be to set custom error code for particular error and then assert.
Testing GraphQL using Rest Assured
Rest Assured allows us to test REST APIs easily and is well known in the Java world for automating REST APIs. Good news (!) is that you could still use Rest Assured to test for GraphQL APIs with one condition though! The GraphQL query(request) in itself is not a JSON payload and here is how you could achieve:
- Firstly, convert the GraphQL query into Stringified JSON format.
- Secondly, pass the converted String as Body parameters to the Request.
- Then finally, validate the response.
@BeforeClass public static void createRequestSpecification() { requestSpecification = new RequestSpecBuilder(). setBaseUri("http://localhost"). setPort(4040). build(); } @Test public void SimpleTest() { given() .spec(requestSpecification) .accept(ContentType.JSON) .contentType(ContentType.JSON) .body("{\"query\":\"query {\\n movies{\\n name\\n }\\n}\",\"variables\":{}}" ) .post("/graphql") .then() .statusCode(200).log().all(); }
💸 Tip: The Generate Code Snippets option in Postman will help you get the Stringified JSON.
Testing GraphQL using Rest Sharp
[SetUp] public void Setup() { _restClient = new RestClient("http://localhost:4040"); } [Test] public void TestGraphQL() { //Arrange _restRequest = new RestRequest("/graphql" , Method.POST); _restRequest.AddHeader("Content-Type", "application/json"); _restRequest.AddParameter("application/json", "{\"query\":\"{\\n movie(id:\\\"5ec2caaaa0f98451a25d1429\\\") " + "{\\n name\\n id\\n actor {\\n name\\n age\\n id\\n }\\n }\\n}\\n\"," + "\"variables\":{}}", ParameterType.RequestBody); //Act _restResponse = _restClient.Execute(_restRequest); //Assert //do validation Console.WriteLine("Printing results for fun : "+_restResponse.Content); }
Testing GraphQL using Karate
The Karate framework has built-in support for testing GraphQL API and it’s seamless. Unlike the other options that we saw above. If you are using Karate for API testing then, it’s very straight forward to test, you can pass the GraphQL Query as it is and validate the response using Karate’s match
assertions.
Scenario: GraphQL request to get all data fields # note the use of text instead of def since this is NOT json Given text query = """ { movies{ name genre actor{ name age id } } } """ And request { query: '#(query)' } When method POST # pretty print the response * print 'response:', response # json-path makes it easy to focus only on the parts you are interested in # which is especially useful for graph-ql as responses tend to be heavily nested # '$' happens to be a JsonPath-friendly short-cut for the 'response' variable #* match $.data.movie.name == 'The Matrix' # the '..' wildcard is useful for traversing deeply nested parts of the json * def actor0 = get[0] response..actor * match actor0 contains { name: 'Keanu Reeves', id: '5ec2ac72abe66a4b8a184f96', age: 56 } * def actor2 = get[2] response..actor * match actor2 contains { name: 'Tom Hanks', id: '5ec2abab84395b4b4c71ed9d', age: 63 }
Testing GraphQL using TestProject
TestProject is a cloud-hosted, 100% free test automation platform built on Selenium and Appium for all testers and developers. It has also got Add-ons that users can use to do additional testing on the same cloud. Here, we can use the community Add-on called RESTful API Client and invoke a POST call to query a GraphQL API. Please follow the steps as mentioned in this blog post.
This is how your test would be, a very simple test to hit the endpoint using POST call and pass the Stringified JSON payload into the Body and you are done.
Once you execute the tests, the tests are executed in seconds and you could verify it from the Reports sections as well. A sample response as below:
Testing GraphQL using Test GraphQL Java
If you are not using any of the above BDD frameworks for API testing and looking for a standard Java project to test GraphQL APIs. Test-GraphQL-Java project by Vimal Selvam should be easy to implement. Simply add the below maven dependency to your Java project and you are done.
<dependency> <groupId>com.vimalselvam</groupId> <artifactId>test-graphql-java</artifactId> <version>1.0.0</version> </dependency> package test.graphql; import java.io.*; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import com.vimalselvam.graphql.GraphqlTemplate; import org.testng.Assert; import org.testng.annotations.Test; import okhttp3.*; /** * Test */ public class TestGraphQL { private static final OkHttpClient client = new OkHttpClient(); private final String graphqlUri = "https://n7b67.sse.codesandbox.io/graphql"; private Response prepareResponse(String graphqlPayload) throws IOException { RequestBody body = RequestBody.create(MediaType.get("application/json; charset=utf-8"), graphqlPayload); Request request = new Request.Builder().url(graphqlUri).post(body).build(); return client.newCall(request).execute(); } @Test public void testGraphqlWithInputStream() throws IOException { // Read a graphql file as an input stream InputStream iStream = TestGraphQL.class.getResourceAsStream("/movies.graphql"); // Create a variables to pass to the graphql query ObjectNode variables = new ObjectMapper().createObjectNode(); variables.put("id", "5ec2caaaa0f98451a25d1429"); // Now parse the graphql file to a request payload string String graphqlPayload = GraphqlTemplate.parseGraphql(iStream, variables); // Build and trigger the request Response response = prepareResponse(graphqlPayload); Assert.assertEquals(response.code(), 200, "Response Code Assertion"); String jsonData = response.body().string(); JsonNode jsonNode = new ObjectMapper().readTree(jsonData); System.out.println(jsonData); Assert.assertEquals(jsonNode.get("data").get("movie").get("name").asText(), "The Matrix"); } }
Hi Manoj, First of all a very nice and comprehensive article. I am QA Automation Engg, I have been working on graphql with RestSharp, actually I am facing difficulties in performing request with body with variables, In this article you have mention variables in payload but Could you please gimme more insights on it.