1

In a quarkus project with quarkus-smallrye-graphql lib, is there a way to unit test a GraphQL resource object like this one :

@GraphQLApi
public class ProductResource {

    @Inject
    private ProductRepository productRepository;

    @Query("products")
    @Description("Get all Products")
    @RolesAllowed({"USER","ADMIN"})
    public List<Product> findAll() {
        return this.productRepository.findAll().list();
    }

    @Mutation
    @Description("Create a new Product")
    @RolesAllowed("ADMIN")
    public Boolean createProduct(String name, Double price) {
        return this.productRepository.createProduct(name, price);
    }
}

I want to be able to send a GraphQL query (with Authentication inactivated/or not) in unit testing in order to validate my annotations but I don't find any documented way to do it.

Fabien MIFSUD
  • 335
  • 5
  • 14

3 Answers3

2

There is a great example documented at https://claudioaltamura.de/api/graphql-apis-with-quarkus with source code at https://github.com/claudioaltamura/quarkus-graphql-superheroes. Based on that when using the official demo code at https://quarkus.io/guides/smallrye-graphql we can write this test:


import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.apache.commons.text.StringEscapeUtils;
import org.junit.jupiter.api.Test;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;

class GqlTestHelpers {

    @SneakyThrows
    public static String loadQueryAsJson(String resource) {
        Path path = Paths.get(resource);
        String allLines = Files.readAllLines(path).stream().collect(Collectors.joining("\n"));
        val quotedQuery = StringEscapeUtils.escapeJson(allLines);
        val json = "{ \"query\": \"" + quotedQuery + "\"}";
        return json;
    }
}

@QuarkusTest
@Slf4j
public class FilmResourceTest {

    @Test
    public void testQuery() {

        val query = GqlTestHelpers.loadQueryAsJson("src/test/resources/allFilms.gql");

        final Response response = RestAssured.given()
                .contentType(ContentType.JSON)
                .body(query)
                .when()
                .post("/graphql")
                .then()
                .assertThat()
                .statusCode(200)
                .and()
                .extract()
                .response();

        log.info(response.body().prettyPrint());

        final List<Film> films = response.jsonPath().getList("data.allFilms", Film.class);
        log.info(films.toString());

        assertThat(films)
                .isNotEmpty()
                .hasSize(3)
                .extracting(Film::getTitle)
                .contains("A New Hope", "The Empire Strikes Back", "Return Of The Jedi");
    }
}

The very powerful assert statement at the end uses the amazing assertj library:

    <dependency>
      <groupId>org.assertj</groupId>
      <artifactId>assertj-core</artifactId>
      <version>3.20.2</version>
      <scope>test</scope>
    </dependency>
simbo1905
  • 6,321
  • 5
  • 58
  • 86
1

If using plain HTTP requests for the communication is ok to you, then something like this would work

@QuarkusTest
public class ProductRepositoryTest {

    @Test
    public void testQuery() {
        RestAssured.given()
                .when()
                .contentType("application/json")
                .body("{ \"query\": \"{" +
                        "  products {" +
                        "    name" +
                        "    price" +
                        "  }" +
                        "}\"" +
                        "}")
                .post("/graphql")
                .then()
                .statusCode(200)
                .body("data.products", Matchers.not(Matchers.emptyArray()));
    }
}

There is also the option to use the typesafe GraphQL client from SmallRye GraphQL (see an example in my repo: https://github.com/jmartisk/mock-artifacts/tree/master/graphql/graphql-client) - it should be usable for testing purposes, even though it's very high level so you might lose some finer-grained functionality. Soon, there will also be a different type of client - the dynamic client, but SmallRye GraphQL doesn't support it yet.

Jan Martiška
  • 1,151
  • 5
  • 7
  • My first try gave a successful test but the log displays : 2021-03-11 08:55:23,632 ERROR [io.sma.graphql] (vert.x-worker-thread-0) SRGQL012000: Data Fetching Error: io.quarkus.security.UnauthorizedException at io.quarkus.security.runtime.interceptor.check.RolesAllowedCheck.apply(RolesAllowedCheck.java:59) I'll get deeper into this point and gave a working solution if I find one. – Fabien MIFSUD Mar 11 '21 at 07:57
0

Last successful try :

  RestAssured.given()
                .when()
                .contentType("application/json")
                .body("{ \"query\": \"{" +
                        "  products {" +
                        "    name" +
                        "    price" +
                        "  }" +
                        "}\"" +
                        "}")
                .post("/graphql")
                .then().log().ifValidationFails()
                .statusCode(200)
                .body("data.products.name", Matchers.hasItems("Ten", "Twenty"))
                .body("data.products.price", Matchers.hasItems(Matchers.equalTo(10f), Matchers.equalTo(20f)));

NB: I've inactivated the security to make the unit tests work.

Fabien MIFSUD
  • 335
  • 5
  • 14