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:

Gradle
dependencies {
	// ...
	testImplementation 'org.springframework.graphql:spring-graphql-test:1.0.0-M5'
}
Maven
<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.