It’s possible to test GraphQL requests with Spring’s WebTestClient
, just sending and
receiving JSON, but a number of GraphQL specific details make this approach more
cumbersome than is necessary.
To get the full testing support, you’ll need to add the spring-graphql-test
dependdency
in your build:
dependencies {
// ...
testImplementation 'org.springframework.graphql:spring-graphql-test:1.0.0-M5'
}
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<version>1.0.0-M5</version>
<scope>test</scope>
</dependency>
</dependencies>
GraphQlTester
GraphQlTester
defines a workflow to test GraphQL requests with the following benefits:
-
Verify no unexpected errors under the "errors" key in the response.
-
Decode under the "data" key in the response.
-
Use JsonPath to decode different parts of the response.
-
Test subscriptions.
To create GraphQlTester
, you only need a GraphQlService
, and no transport:
GraphQlSource graphQlSource = GraphQlSource.builder()
.schemaResources(...)
.runtimeWiringConfigurer(...)
.build();
GraphQlService graphQlService = new ExecutionGraphQlService(graphQlSource);
GraphQlTester graphQlTester = GraphQlTester.builder(graphQlService).build();
WebGraphQlTester
WebGraphQlTester
extends GraphQlTester
to add a workflow and configuration specific
to index.html, and it always verifies GraphQL HTTP responses are 200 (OK).
To create WebGraphQlTester
, you need one of the following inputs:
-
WebTestClient
— perform requests as an HTTP client, either against index.html handlers without a server, or against a live server. -
WebGraphQlHandler
— perform requests through the index.html chain used by both index.html and index.html handlers, which in effect is testing without a Web framework. One reason to use this is for Subscriptions.
For Spring WebFlux without a server, you can point to your Spring configuration:
ApplicationContext context = ... ;
WebTestClient client =
WebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
For Spring MVC without a server, the same but using MockMvcWebTestClient
:
WebApplicationContext context = ... ;
WebTestClient client =
MockMvcWebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
To test against a live, running server:
WebTestClient client =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql")
.build();
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
WebGraphQlTester
supports setting HTTP request headers and access to HTTP response
headers. This may be useful to inspect or set security related headers.
this.graphQlTester.queryName("{ myQuery }")
.httpHeaders(headers -> headers.setBasicAuth("rob", "..."))
.execute()
.httpHeadersSatisfy(headers -> {
// check response headers
})
.path("myQuery.field1").entity(String.class).isEqualTo("value1")
.path("myQuery.field2").entity(String.class).isEqualTo("value2");
You can also set default request headers at the builder level:
WebGraphQlTester tester = WebGraphQlTester.builder(client)
.defaultHttpHeaders(headers -> headers.setBasicAuth("rob", "..."))
.build();
Queries
Below is an example query test using JsonPath to extract all release versions in the GraphQL response.
String query = "{" +
" project(slug:\"spring-framework\") {" +
" releases {" +
" version" +
" }"+
" }" +
"}";
graphQlTester.query(query)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
The JsonPath is relative to the "data" section of the response.
You can also create query files with extensions .graphql
or .gql
under "graphql/"
on
the classpath and refer to them by file name. For example, given a file called
projectReleases.graphql
in src/main/resources/graphql
, with content:
query projectReleases($slug: ID!) {
project(slug: $slug) {
releases {
version
}
}
}
You can write the same test as follows:
graphQlTester.queryName("projectReleases") (1)
.variable("slug", "spring-framework") (2)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
1 | Refer to the query in the file named "projectReleases". |
2 | Set the slug variable. |
The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion. |
Errors
Verify won’t succeed when there are errors under the "errors" key in the response.
If necessary to ignore an error, use an error filter Predicate
:
graphQlTester.query(query)
.execute()
.errors()
.filter(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
An error filter can be registered globally and apply to all tests:
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
.errorFilter(error -> ...)
.build();
Or to expect an error, and in contrast to filter
, throw an assertion error
when it doesn’t exist in the response:
graphQlTester.query(query)
.execute()
.errors()
.expect(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
Or inspect all errors directly and that also marks them as filtered:
graphQlTester.query(query)
.execute()
.errors()
.satisfy(errors -> {
// ...
});
If a request does not have any response data (e.g. mutation), use executeAndVerify
instead of execute
to verify there are no errors in the response:
graphQlTester.query(query).executeAndVerify();
Subscriptions
The executeSubscription
method defines a workflow specific to subscriptions which return
a stream of responses instead of a single response.
To test subscriptions, you can create GraphQlTester
with a GraphQlService
, which
calls graphql.GraphQL
directly and that returns a stream of responses:
GraphQlService service = ... ;
GraphQlTester graphQlTester = GraphQlTester.builder(service).build();
Flux<String> result = graphQlTester.query("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class); // decode each response
The StepVerifier
from Project Reactor is useful to verify a stream:
Flux<String> result = graphQlTester.query("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class);
StepVerifier.create(result)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
To test with the index.html chain, you can create WebGraphQlTester
with a
WebGraphQlHandler
:
GraphQlService service = ... ;
WebGraphQlHandler handler = WebGraphQlHandler.builder(service)
.interceptor((input, next) -> next.handle(input))
.build();
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(handler).build();
Currently, Spring for GraphQL does not support testing with a WebSocket client, and it cannot be used for integration test of GraphQL over WebSocket requests.