Testing
面向 GraphQL 的 Spring 为针对 HTTP、WebSocket 和 RSocket 测试 GraphQL 请求以及针对服务器直接测试提供了专门的支持。
Spring for GraphQL provides dedicated support for testing GraphQL requests over HTTP, WebSocket, and RSocket, as well as for testing directly against a server.
要使用此功能,请将 spring-graphql-test
添加到你的生成中:
To make use of this, add spring-graphql-test
to your build:
-
Gradle
-
Maven
dependencies {
// ...
testImplementation 'org.springframework.graphql:spring-graphql-test:{spring-graphql-version}'
}
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<version>{spring-graphql-version}</version>
<scope>test</scope>
</dependency>
</dependencies>
GraphQlTester
GraphQlTester
是一个契约,它声明了一个独立于底层传输的测试 GraphQL 请求的通用工作流。这意味着无论底层传输是什么,都会使用相同的 API 测试请求,并且在生成时配置任何传输特定信息。
GraphQlTester
is a contract that declares a common workflow for testing GraphQL
requests that is independent of the underlying transport. That means requests are tested
with the same API no matter what the underlying transport, and anything transport
specific is configured at build time.
要创建通过客户端执行请求的 GraphQlTester
,你需要以下扩展之一:
To create a GraphQlTester
that performs requests through a client, you need one of the
following extensions:
要创建在服务器端执行测试的 GraphQlTester
,而不使用客户端:
To create a GraphQlTester
that performs tests on the server side, without a client:
每个都使用与传输相关的选项定义了 Builder
。所有的构建程序都扩展自具有与所有扩展相关的选项的公共的基本的 GraphQlTester Builder
。
Each defines a Builder
with options relevant to the transport. All builders extend
from a common, base GraphQlTester Builder
with
options relevant to all extensions.
HTTP
HttpGraphQlTester
使用 WebTestClient 在没有或有实时服务器的情况下通过 HTTP 执行 GraphQL 请求,具体取决于 WebTestClient
的配置方式。
HttpGraphQlTester
uses
WebTestClient to execute
GraphQL requests over HTTP, with or without a live server, depending on how
WebTestClient
is configured.
若要在 Spring WebFlux 中进行测试,没有实时服务器,请指向声明 GraphQL HTTP 端点的 Spring 配置:
To test in Spring WebFlux, without a live server, point to your Spring configuration that declares the GraphQL HTTP endpoint:
ApplicationContext context = ... ;
WebTestClient client =
WebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
若要在 Spring MVC 中进行测试,没有实时服务器,使用`MockMvcWebTestClient`执行相同操作:
To test in Spring MVC, without a live server, do the same using MockMvcWebTestClient
:
ApplicationContext context = ... ;
WebTestClient client =
MockMvcWebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
或针对在端口上运行的实时服务器进行测试:
Or to test against a live server running on a port:
WebTestClient client =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
一旦创建了 HttpGraphQlTester
,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。如果您需要更改任何传输特定详细信息,请对现有的 HttpSocketGraphQlTester
使用 mutate()
以创建具有自定义设置的新实例:
Once HttpGraphQlTester
is created, you can begin to
execute requests using the same API, independent of the underlying
transport. If you need to change any transport specific details, use mutate()
on an
existing HttpSocketGraphQlTester
to create a new instance with customized settings:
HttpGraphQlTester tester = HttpGraphQlTester.builder(clientBuilder)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
HttpGraphQlTester anotherTester = tester.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
WebSocket
WebSocketGraphQlTester
通过共享 WebSocket 连接执行 GraphQL 请求。它是使用 Spring WebFlux 中的 WebSocketClient 构建的,可以按以下方式创建:
WebSocketGraphQlTester
executes GraphQL requests over a shared WebSocket connection.
It is built using the
WebSocketClient
from Spring WebFlux and you can create it as follows:
String url = "http://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client).build();
`WebSocketGraphQlTester`面向连接,并且是多路复用的。每个实例为所有请求建立自己的单个共享连接。通常,你只想针对每个服务器使用单个实例。
WebSocketGraphQlTester
is connection oriented and multiplexed. Each instance establishes
its own single, shared connection for all requests. Typically, you’ll want to use a single
instance only per server.
一旦创建了 WebSocketGraphQlTester
,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。如果您需要更改任何传输特定详细信息,请对现有的 WebSocketGraphQlTester
使用 mutate()
以创建具有自定义设置的新实例:
Once WebSocketGraphQlTester
is created, you can begin to
execute requests using the same API, independent of the underlying
transport. If you need to change any transport specific details, use mutate()
on an
existing WebSocketGraphQlTester
to create a new instance with customized settings:
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
WebSocketGraphQlTester anotherTester = tester.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
`WebSocketGraphQlTester`提供了一个`stop()`方法,你可以使用该方法关闭 WebSocket 连接,例如,在测试运行之后。
WebSocketGraphQlTester
provides a stop()
method that you can use to have the WebSocket
connection closed, e.g. after a test runs.
RSocket
RSocketGraphQlTester`使用 spring-messaging 中的 `RSocketRequester
通过 RSocket 执行 GraphQL 请求:
RSocketGraphQlTester
uses RSocketRequester
from spring-messaging to execute GraphQL
requests over RSocket:
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlTester client = RSocketGraphQlTester.builder()
.clientTransport(transport)
.build();
`RSocketGraphQlTester`面向连接,并且是多路复用的。每个实例为所有请求建立自己的单个共享会话。通常,你只想针对每个服务器使用单个实例。你可以使用测试器上的`stop()`方法显式关闭会话。
RSocketGraphQlTester
is connection oriented and multiplexed. Each instance establishes
its own single, shared session for all requests. Typically, you’ll want to use a single
instance only per server. You can use the stop()
method on the tester to close the
session explicitly.
一旦创建了 RSocketGraphQlTester
,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。
Once RSocketGraphQlTester
is created, you can begin to
execute requests using the same API, independent of the underlying
transport.
ExecutionGraphQlService
许多时候直接在服务器端测试 GraphQL 请求就已足够,而无需使用客户端通过传输协议发送请求。若要针对`ExecutionGraphQlService`直接进行测试,请使用`ExecutionGraphQlServiceTester`扩展:
Many times it’s enough to test GraphQL requests on the server side, without the use of a
client to send requests over a transport protocol. To test directly against a
ExecutionGraphQlService
, use the ExecutionGraphQlServiceTester
extension:
ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.create(service);
一旦创建了 ExecutionGraphQlServiceTester
,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。
Once ExecutionGraphQlServiceTester
is created, you can begin to
execute requests using the same API, independent of the underlying
transport.
`ExecutionGraphQlServiceTester.Builder`提供了一个选项来自定义`ExecutionInput`详细信息:
ExecutionGraphQlServiceTester.Builder
provides an option to customize ExecutionInput
details:
ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.builder(service)
.configureExecutionInput((executionInput, builder) -> builder.executionId(id).build())
.build();
WebGraphQlHandler
ExecutionGraphQlService
扩展允许您在没有客户端的情况下在服务器端进行测试。但是,在某些情况下,使用给定的模拟传输输入涉及服务器端传输处理会很有用。
The ExecutionGraphQlService
extension lets you test on the server side, without
a client. However, in some cases it’s useful to involve server side transport
handling with given mock transport input.
`WebGraphQlTester`扩展使你能够在传递给`ExecutionGraphQlService`执行请求之前,通过`WebGraphQlInterceptor`链处理请求:
The WebGraphQlTester
extension lets you processes request through the
WebGraphQlInterceptor
chain before handing off to ExecutionGraphQlService
for
request execution:
WebGraphQlHandler handler = ... ;
WebGraphQlTester tester = WebGraphQlTester.create(handler);
此扩展的构建器允许你定义 HTTP 请求详细信息:
The builder for this extension allows you to define HTTP request details:
WebGraphQlHandler handler = ... ;
WebGraphQlTester tester = WebGraphQlTester.builder(handler)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
一旦创建了 WebGraphQlTester
,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。
Once WebGraphQlTester
is created, you can begin to
execute requests using the same API, independent of the underlying transport.
Builder
GraphQlTester`定义了一个父`Builder
,其中包含所有扩展构建器的通用配置选项。它允许你配置以下内容:
GraphQlTester
defines a parent Builder
with common configuration options for the
builders of all extensions. It lets you configure the following:
-
errorFilter
- a predicate to suppress expected errors, so you can inspect the data of the response. -
documentSource
- a strategy for loading the document for a request from a file on the classpath or from anywhere else. -
responseTimeout
- how long to wait for request execution to complete before timing out.
Requests
一旦有了 GraphQlTester
,你就可以开始对请求进行测试。下面是为项目执行查询并使用 JsonPath 从响应中提取项目发布版本的示例:
Once you have a GraphQlTester
, you can begin to test requests. The below executes a
query for a project and uses JsonPath to extract
project release versions from the response:
String document = "{" +
" project(slug:\"spring-framework\") {" +
" releases {" +
" version" +
" }"+
" }" +
"}";
graphQlTester.document(document)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
JsonPath 相对于响应的 “data” 部分。
The JsonPath is relative to the "data" section of the response.
您也可以在类路径的 “graphql-test/” 下创建扩展名为 .graphql
或 .gql
的文档文件,并通过文件名引用它们。
You can also create document files with extensions .graphql
or .gql
under
"graphql-test/"
on the classpath and refer to them by file name.
例如,给定在 src/main/resources/graphql-test
中名为 projectReleases.graphql
的文件,其内容为:
For example, given a file called projectReleases.graphql
in
src/main/resources/graphql-test
, with content:
query projectReleases($slug: ID!) {
project(slug: $slug) {
releases {
version
}
}
}
然后,您可以使用:
You can then use:
graphQlTester.documentName("projectReleases") 1
.variable("slug", "spring-framework") 2
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
1 | Refer to the document in the file named "project". |
2 | Set the slug variable. |
IntelliJ 的 “JS GraphQL” 插件支持带有代码补全功能的 GraphQL 查询文件。 The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion. |
如果请求没有任何响应数据,例如变异,那么使用 executeAndVerify
代替 execute
来验证响应中没有错误:
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();
有关错误处理的更多详细信息,请参见 Errors。
See Errors for more details on error handling.
Nested Paths
默认情况下,路径相对于 GraphQL 响应的 “data” 部分。您还可以缩小到某个路径,并检查相对于它的多个路径,如下所示:
By default, paths are relative to the "data" section of the GraphQL response. You can also nest down to a path, and inspect multiple paths relative to it as follows:
graphQlTester.document(document)
.execute()
.path("project", project -> project (1)
.path("name").entity(String.class).isEqualTo("spring-framework")
.path("releases[*].version").entityList(String.class).hasSizeGreaterThan(1));
1 | Use a callback to inspect paths relative to "project". |
Subscriptions
要测试订阅,请调用 executeSubscription
代替 execute
来获取响应流,然后使用 Project Reactor 中的 StepVerifier
来检查流:
To test subscriptions, call executeSubscription
instead of execute
to obtain a stream
of responses and then use StepVerifier
from Project Reactor to inspect the stream:
Flux<String> greetingFlux = tester.document("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class); // decode at JSONPath
StepVerifier.create(greetingFlux)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
订阅仅受 WebSocketGraphQlTester 支持,或与服务器端 ExecutionGraphQlService
和 WebGraphQlHandler
扩展一起受支持。
Subscriptions are supported only with WebSocketGraphQlTester
, or with the server side
ExecutionGraphQlService
and WebGraphQlHandler
extensions.
Errors
当您使用 verify()
时,响应中的 “errors” 键下的任何错误都将导致断言失败。要抑制特定错误,请在 verify()
之前使用错误筛选器:
When you use verify()
, any errors under the "errors" key in the response will cause
an assertion failure. To suppress a specific error, use the error filter before
verify()
:
graphQlTester.query(query)
.execute()
.errors()
.filter(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
您可以在构建器级别注册错误筛选器,以便应用于所有测试:
You can register an error filter at the builder level, to apply to all tests:
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
.errorFilter(error -> ...)
.build();
如果您想验证是否存在某个错误,并且与 filter
相反,如果它不存在就抛出断言错误,那么使用 expect
代替:
If you want to verify that an error does exist, and in contrast to filter
, throw an
assertion error if it doesn’t, then use expect
instead:
graphQlTester.query(query)
.execute()
.errors()
.expect(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
您还可通过 Consumer
检查所有错误,此操作也会将其标记为已筛选,因此您还可以检查响应中的数据:
You can also inspect all errors through a Consumer
, and doing so also marks them as
filtered, so you can then also inspect the data in the response:
graphQlTester.query(query)
.execute()
.errors()
.satisfy(errors -> {
// ...
});