281 lines
7.6 KiB
Plaintext
281 lines
7.6 KiB
Plaintext
[[testing]]
|
|
= Testing
|
|
|
|
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.
|
|
|
|
|
|
|
|
[[testing-graphqltester]]
|
|
== `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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
GraphQlSource graphQlSource = GraphQlSource.builder()
|
|
.schemaResources(...)
|
|
.runtimeWiringConfigurer(...)
|
|
.build();
|
|
|
|
GraphQlService graphQlService = new ExecutionGraphQlService(graphQlSource);
|
|
|
|
GraphQlTester graphQlTester = GraphQlTester.builder(graphQlService).build();
|
|
----
|
|
|
|
|
|
|
|
[[testing-webgraphqltester]]
|
|
== `WebGraphQlTester`
|
|
|
|
`WebGraphQlTester` extends `GraphQlTester` to add a workflow and configuration specific
|
|
to <<index#web-transports>>, 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#web-http>>
|
|
handlers without a server, or against a live server.
|
|
- `WebGraphQlHandler` -- perform requests through the <<index#web-interception>> chain used
|
|
by both <<index#web-http>> and <<index#web-websocket>> handlers, which in effect is testing without
|
|
a Web framework. One reason to use this is for <<testing-subscriptions>>.
|
|
|
|
For Spring WebFlux without a server, you can point to your Spring configuration:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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`:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
WebApplicationContext context = ... ;
|
|
|
|
WebTestClient client =
|
|
MockMvcWebTestClient.bindToApplicationContext(context)
|
|
.configureClient()
|
|
.baseUrl("/graphql")
|
|
.build();
|
|
|
|
WebGraphQlTester tester = WebGraphQlTester.builder(client).build();
|
|
----
|
|
|
|
To test against a live, running server:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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.
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
WebGraphQlTester tester = WebGraphQlTester.builder(client)
|
|
.defaultHttpHeaders(headers -> headers.setBasicAuth("rob", "..."))
|
|
.build();
|
|
----
|
|
|
|
|
|
|
|
[[testing-queries]]
|
|
== Queries
|
|
|
|
Below is an example query test using
|
|
https://github.com/json-path/JsonPath[JsonPath] to extract all release versions in the
|
|
GraphQL response.
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,graphql,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
query projectReleases($slug: ID!) {
|
|
project(slug: $slug) {
|
|
releases {
|
|
version
|
|
}
|
|
}
|
|
}
|
|
----
|
|
|
|
You can write the same test as follows:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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.
|
|
|
|
[TIP]
|
|
====
|
|
The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion.
|
|
====
|
|
|
|
|
|
|
|
[[testing-errors]]
|
|
== Errors
|
|
|
|
Tests cannot use verify data, if there are errors under the "errors" key in the response
|
|
has errors. If necessary to ignore an error, use an error filter `Predicate`:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
|
|
.errorFilter(error -> ...)
|
|
.build();
|
|
----
|
|
|
|
Or inspect all errors directly and that also marks them as filtered:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
graphQlTester.query(query).executeAndVerify();
|
|
----
|
|
|
|
|
|
|
|
[[testing-subscriptions]]
|
|
== 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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
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#web-interception>> chain, you can create `WebGraphQlTester` with a
|
|
`WebGraphQlHandler`:
|
|
|
|
[source,java,indent=0,subs="verbatim,quotes"]
|
|
----
|
|
GraphQlService service = ... ;
|
|
|
|
WebGraphQlHandler handler = WebGraphQlHandler.builder(service)
|
|
.interceptor((input, next) -> next.handle(input))
|
|
.build();
|
|
|
|
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(handler).build();
|
|
----
|
|
|
|
Currently, Spring GraphQL does not support testing with a WebSocket client, and it
|
|
cannot be used for integration test of GraphQL over WebSocket requests.
|