DEV Community

Vimalraj Selvam
Vimalraj Selvam

Posted on • Originally published at vimalselvam.com on

Introducing Test GraphQL Java

I was recently asked by one of my friend how can we test the GraphQL APIs in Java. He is currently exploring Karate's capability, however, he doesn’t want to use Karate just for this as they’re already using TestNG based framework. And there is a graphql-java library which let’s you to implement GraphQL in Java and test, but using Spring Boot. My main goal is not to introduce Spring Boot just for the sake of testing the GraphQL API. I wanted to keep it simple!

Goals were:

  • Should not bring various dependencies (this library currently depends only on jackson).
  • Should just turn the graphql file into request payload string.
  • Should be able to use any HTTP client library.
  • Should be able to use any Java testing framework.

That is how this library born. Let’s directly jump in how can we use it.

Getting Started

Adding maven dependency:

<dependency>
    <groupId>com.vimalselvam</groupId>
    <artifactId>test-graphql-java</artifactId>
    <version>1.0.0</version>
</dependency>

I don’t use Gradle, but it should be straight forward to add this library as a Gradle dependency.

Let’s test the Pokemon GraphQL API. We’re going to test the following query:

query pokemon {
  pokemon(name: "Pikachu") {
    name
  }
}

We’ll trigger this query and assert the successful response code and the response body where the name key contains Pikachu.

Take the above query and put it into a pokemon.graphql under src/test/resources/graphql/ in your maven project directory.

We can load this file in our test using either of the following two ways:

  • using InputStream:
InputStream iStream = getClass().getResourceAsStream("/graphql/pokemon.graphql");
  • using File:
File file = new File("src/test/resources/graphql/pokemon.graphql");

For this example, I’m using the 2nd approach, File.

Once you read the file, just pass it to GraphqlTemplate class to parse as follows:

String graphqlPayload = GraphqlTemplate.parseGraphql(file, null);

The 2nd argument is variables which is used to parameterize your GraphQL query. I’ll show you how to use that in a short while, till that let’s keep it null.

That’s it! You now have the graphql query string which you can directly pass as a request payload on your preferred HTTP client library.

Let’s talk about variables. The GraphQL has a feature to set some variables and pass those variables at run time during execution of query. For that, we’ll modify our Pokemon query.

Open the pokemon.graphql file and change it as:

query pokemon($name:String!) {
  pokemon(name: $name) {
    name
  }
}

Here $name is the variable and it accepts only String. The ! operator denotes that this is mandatory variable. Let’s see how we can handle this variables scenario in our code.

Before converting the GraphQL query to the plain string, let’s create the variables:

ObjectNode variables = new ObjectMapper().createObjectNode();
variables.put("name", "Pikachu");

Here we’re using com.fasterxml.jackson.databind.node.ObjectNode to create variables. This ObjectNode can be passed as 2nd parameter in our GraphqlTemplate class.

String graphqlPayload = GraphqlTemplate.parseGraphql(file, variables);

That simple it is!

I’ve open sourced this library and can be found here: https://github.com/vimalrajselvam/test-graphql-java.

Contributions are welcome. If you have any thoughts or issues, kindly open an issue in the above github link.

Let’s see the full example code:

package com.vimalselvam.graphql;

import java.io.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import org.testng.Assert;
import org.testng.annotations.Test;

import okhttp3.*;

public class TestClass {
    private static final OkHttpClient client = new OkHttpClient();
    private final String graphqlUri = "https://graphql-pokemon.now.sh/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 = TestClass.class.getResourceAsStream("/graphql/pokemon.graphql");

        // Create a variables to pass to the graphql query
        ObjectNode variables = new ObjectMapper().createObjectNode();
        variables.put("name", "Pikachu");

        // 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);
        Assert.assertEquals(jsonNode.get("data").get("pokemon").get("name").asText(), "Pikachu");
    }

    @Test
    public void testGraphqlWithFile() throws IOException {
        // Read a graphql file
        File file = new File("src/test/resources/graphql/pokemon.graphql");

        // Create a variables to pass to the graphql query
        ObjectNode variables = new ObjectMapper().createObjectNode();
        variables.put("name", "Pikachu");

        // Now parse the graphql file to a request payload string
        String graphqlPayload = GraphqlTemplate.parseGraphql(file, 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);
        Assert.assertEquals(jsonNode.get("data").get("pokemon").get("name").asText(), "Pikachu");
    }

    @Test
    public void testGraphqlWithNoVariables() throws IOException {
        // Read a graphql file
        File file = new File("src/test/resources/graphql/pokemon-with-no-variable.graphql");

        // Now parse the graphql file to a request payload string
        String graphqlPayload = GraphqlTemplate.parseGraphql(file, null);

        // 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);
        Assert.assertEquals(jsonNode.get("data").get("pokemon").get("name").asText(), "Pikachu");
    }
}

Top comments (3)

Collapse
 
ptrthomas profile image
Peter Thomas

Not using Karate because you already use TestNG sounds like a very wrong decision to me because Karate has IDE support and can be executed from plain Java, search the docs for "Parallel Execution".

And notice how your assertions are really basic, just the response code (200 OK) and one key-value out of what potentially could be a large, complex, nested JSON (typical in GraphQL).

Please refer this video to your friend (10 min) for more details: youtu.be/If9V-tG_gRs

Collapse
 
email2vimalraj profile image
Vimalraj Selvam

Hi Peter,

Karate is a great framework, but we already have a framework which is working perfectly from long. If that sounds a wrong decision to you, I don't have any words to reply.

Regarding the simpler assertion, that is a basic example given. The idea of this library is letting user to select any framework and http client library.

And btw, if possible, cover the presentation of your video clearly. We barely see the presentation, not clear, hurts my eyes for this 10 mins talk.

Collapse
 
ptrthomas profile image
Peter Thomas

No worries, the intent is not to convince you as your mindset is clear from your response :)

I am trying to put forward data for readers who may be misled by the information presented here. I leave it to readers to decide the merit of your argument - which is that you have some existing framework in place. GraphQL needs a different approach, which IMO is clear in the presentation. Sorry it hurts your eyes :) But there are plenty more videos and examples for those who care to look, and here is a reference link - which also points to the source-code in the presentation:

Anyway, all the best with using TestNG and JsonNode to test GraphQL :P