Testing

面向 GraphQL 的 Spring 为针对 HTTP、WebSocket 和 RSocket 测试 GraphQL 请求以及针对服务器直接测试提供了专门的支持。 要使用此功能,请将 spring-graphql-test 添加到你的生成中:

  • 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,你需要以下扩展之一:

要创建在服务器端执行测试的 GraphQlTester,而不使用客户端:

每个都使用与传输相关的选项定义了 Builder。所有的构建程序都扩展自具有与所有扩展相关的选项的公共的基本的 GraphQlTester Builder

HTTP

HttpGraphQlTester 使用 WebTestClient 在没有或有实时服务器的情况下通过 HTTP 执行 GraphQL 请求,具体取决于 WebTestClient 的配置方式。

若要在 Spring WebFlux 中进行测试,没有实时服务器,请指向声明 GraphQL HTTP 端点的 Spring 配置:

ApplicationContext context = ... ;

WebTestClient client =
		WebTestClient.bindToApplicationContext(context)
				.configureClient()
				.baseUrl("/graphql")
				.build();

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

若要在 Spring MVC 中进行测试,没有实时服务器,使用`MockMvcWebTestClient`执行相同操作:

ApplicationContext context = ... ;

WebTestClient client =
		MockMvcWebTestClient.bindToApplicationContext(context)
				.configureClient()
				.baseUrl("/graphql")
				.build();

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

或针对在端口上运行的实时服务器进行测试:

WebTestClient client =
		WebTestClient.bindToServer()
				.baseUrl("http://localhost:8080/graphql")
				.build();

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

一旦创建了 HttpGraphQlTester,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。如果您需要更改任何传输特定详细信息,请对现有的 HttpSocketGraphQlTester 使用 mutate() 以创建具有自定义设置的新实例:

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 构建的,可以按以下方式创建:

String url = "http://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();

WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client).build();

`WebSocketGraphQlTester`面向连接,并且是多路复用的。每个实例为所有请求建立自己的单个共享连接。通常,你只想针对每个服务器使用单个实例。

一旦创建了 WebSocketGraphQlTester,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。如果您需要更改任何传输特定详细信息,请对现有的 WebSocketGraphQlTester 使用 mutate() 以创建具有自定义设置的新实例:

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 连接,例如,在测试运行之后。

RSocket

RSocketGraphQlTester`使用 spring-messaging 中的 `RSocketRequester 通过 RSocket 执行 GraphQL 请求:

URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);

RSocketGraphQlTester client = RSocketGraphQlTester.builder()
		.clientTransport(transport)
		.build();

`RSocketGraphQlTester`面向连接,并且是多路复用的。每个实例为所有请求建立自己的单个共享会话。通常,你只想针对每个服务器使用单个实例。你可以使用测试器上的`stop()`方法显式关闭会话。

一旦创建了 RSocketGraphQlTester,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。

ExecutionGraphQlService

许多时候直接在服务器端测试 GraphQL 请求就已足够,而无需使用客户端通过传输协议发送请求。若要针对`ExecutionGraphQlService`直接进行测试,请使用`ExecutionGraphQlServiceTester`扩展:

ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.create(service);

一旦创建了 ExecutionGraphQlServiceTester,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。

`ExecutionGraphQlServiceTester.Builder`提供了一个选项来自定义`ExecutionInput`详细信息:

ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.builder(service)
		.configureExecutionInput((executionInput, builder) -> builder.executionId(id).build())
		.build();

WebGraphQlHandler

ExecutionGraphQlService 扩展允许您在没有客户端的情况下在服务器端进行测试。但是,在某些情况下,使用给定的模拟传输输入涉及服务器端传输处理会很有用。

`WebGraphQlTester`扩展使你能够在传递给`ExecutionGraphQlService`执行请求之前,通过`WebGraphQlInterceptor`链处理请求:

WebGraphQlHandler handler = ... ;
WebGraphQlTester tester = WebGraphQlTester.create(handler);

此扩展的构建器允许你定义 HTTP 请求详细信息:

WebGraphQlHandler handler = ... ;

WebGraphQlTester tester = WebGraphQlTester.builder(handler)
		.headers(headers -> headers.setBasicAuth("joe", "..."))
		.build();

一旦创建了 WebGraphQlTester,就可以使用相同的 API 开始 execute requests,而不需要底层的传输。

Builder

GraphQlTester`定义了一个父`Builder,其中包含所有扩展构建器的通用配置选项。它允许你配置以下内容:

  • errorFilter- 一个用于禁止预期错误的谓词,因此你可以检查响应的数据。

  • documentSource- 从类路径的文件或其他任何位置加载请求文档的策略。

  • responseTimeout- 请求执行完成之前等待多长时间再超时。

Requests

一旦有了 GraphQlTester,你就可以开始对请求进行测试。下面是为项目执行查询并使用 JsonPath 从响应中提取项目发布版本的示例:

String document = "{" +
		"  project(slug:\"spring-framework\") {" +
		"	releases {" +
		"	  version" +
		"	}"+
		"  }" +
		"}";

graphQlTester.document(document)
		.execute()
		.path("project.releases[*].version")
		.entityList(String.class)
		.hasSizeGreaterThan(1);

JsonPath 相对于响应的 “data” 部分。

您也可以在类路径的 “graphql-test/” 下创建扩展名为 .graphql.gql 的文档文件,并通过文件名引用它们。

例如,给定在 src/main/resources/graphql-test 中名为 projectReleases.graphql 的文件,其内容为:

query projectReleases($slug: ID!) {
	project(slug: $slug) {
		releases {
			version
		}
	}
}

然后,您可以使用:

graphQlTester.documentName("projectReleases") 1
		.variable("slug", "spring-framework") 2
		.execute()
		.path("project.releases[*].version")
		.entityList(String.class)
		.hasSizeGreaterThan(1);
1 参考名为“项目”的文件中的文档。
2 Set the slug variable.

IntelliJ 的 “JS GraphQL” 插件支持带有代码补全功能的 GraphQL 查询文件。

如果请求没有任何响应数据,例如变异,那么使用 executeAndVerify 代替 execute 来验证响应中没有错误:

graphQlTester.query(query).executeAndVerify();

有关错误处理的更多详细信息,请参见 Errors

Nested Paths

默认情况下,路径相对于 GraphQL 响应的 “data” 部分。您还可以缩小到某个路径,并检查相对于它的多个路径,如下所示:

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 使用一个回调来检查相对于“项目”的路径。

Subscriptions

要测试订阅,请调用 executeSubscription 代替 execute 来获取响应流,然后使用 Project Reactor 中的 StepVerifier 来检查流:

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 支持,或与服务器端 ExecutionGraphQlServiceWebGraphQlHandler 扩展一起受支持。

Errors

当您使用 verify() 时,响应中的 “errors” 键下的任何错误都将导致断言失败。要抑制特定错误,请在 verify() 之前使用错误筛选器:

graphQlTester.query(query)
		.execute()
		.errors()
		.filter(error -> ...)
		.verify()
		.path("project.releases[*].version")
		.entityList(String.class)
		.hasSizeGreaterThan(1);

您可以在构建器级别注册错误筛选器,以便应用于所有测试:

WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
		.errorFilter(error -> ...)
		.build();

如果您想验证是否存在某个错误,并且与 filter 相反,如果它不存在就抛出断言错误,那么使用 expect 代替:

graphQlTester.query(query)
		.execute()
		.errors()
		.expect(error -> ...)
		.verify()
		.path("project.releases[*].version")
		.entityList(String.class)
		.hasSizeGreaterThan(1);

您还可通过 Consumer 检查所有错误,此操作也会将其标记为已筛选,因此您还可以检查响应中的数据:

graphQlTester.query(query)
		.execute()
		.errors()
		.satisfy(errors -> {
			// ...
		});