测试

Spring for GraphQL 为通过 HTTP 测试 GraphQL 请求提供专用支持, WebSocket 和 RSocket,以及直接针对服务器进行测试。spring-doc.cadn.net.cn

要使用此功能,请添加spring-graphql-test添加到您的 build 中:spring-doc.cadn.net.cn

dependencies {
	// ...
	testImplementation 'org.springframework.graphql:spring-graphql-test:1.3.3'
}
<dependencies>
	<!-- ... -->
	<dependency>
		<groupId>org.springframework.graphql</groupId>
		<artifactId>spring-graphql-test</artifactId>
		<version>1.3.3</version>
		<scope>test</scope>
	</dependency>
</dependencies>

GraphQlTester

GraphQlTester是一个声明用于测试 GraphQL 的通用工作流程的合同 请求,该请求独立于底层传输。这意味着请求已经过测试 使用相同的 API,无论底层传输是什么,以及任何传输 specific 在构建时进行配置。spring-doc.cadn.net.cn

要创建GraphQlTester,您需要 以下扩展名:spring-doc.cadn.net.cn

要创建GraphQlTester在服务器端执行测试,没有客户端:spring-doc.cadn.net.cn

每个 API 都定义了一个Builder以及与运输相关的选项。所有构建器都扩展了 来自通用的 GraphQlTesterBuilder跟 与所有扩展相关的选项。spring-doc.cadn.net.cn

HTTP 协议

HttpGraphQlTester使用 WebTestClient 执行 通过 HTTP 发出的 GraphQL 请求,无论是否有实时服务器,具体取决于方式WebTestClient已配置。spring-doc.cadn.net.cn

要在 Spring WebFlux 中进行测试,在没有实时服务器的情况下,请指向您的 Spring 配置 声明 GraphQL HTTP 终端节点:spring-doc.cadn.net.cn

AnnotationConfigWebApplicationContext context = ...

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

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

要在 Spring MVC 中进行测试,在没有实时服务器的情况下,使用MockMvcWebTestClient:spring-doc.cadn.net.cn

AnnotationConfigWebApplicationContext context = ...

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

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

或者,要针对在端口上运行的实时服务器进行测试:spring-doc.cadn.net.cn

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

HttpGraphQlTester tester = HttpGraphQlTester.create(client);

一次HttpGraphQlTester创建后,您可以开始使用相同的 API 执行请求,独立于底层 运输。如果需要更改任何特定于传输的详细信息,请使用mutate()在 现存HttpSocketGraphQlTester要创建具有自定义设置的新实例:spring-doc.cadn.net.cn

WebTestClient.Builder clientBuilder =
		WebTestClient.bindToServer()
				.baseUrl("http://localhost:8080/graphql");

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 构建的,你可以按如下方式创建它:spring-doc.cadn.net.cn

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

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

WebSocketGraphQlTester面向连接且多路复用。每个实例建立 它自己的单个共享连接,用于所有请求。通常,您需要使用单个 每个服务器仅实例。spring-doc.cadn.net.cn

一次WebSocketGraphQlTester创建后,您可以开始使用相同的 API 执行请求,独立于底层 运输。如果需要更改任何特定于传输的详细信息,请使用mutate()在 现存WebSocketGraphQlTester要创建具有自定义设置的新实例:spring-doc.cadn.net.cn

URI url = URI.create("ws://localhost:8080/graphql");
WebSocketClient client = new ReactorNettyWebSocketClient();

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 连接已关闭,例如在测试运行后。spring-doc.cadn.net.cn

RSocket 系列

RSocketGraphQlTester使用RSocketRequester从 spring-messaging 执行 GraphQL 通过 RSocket 的请求:spring-doc.cadn.net.cn

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

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

RSocketGraphQlTester面向连接且多路复用。每个实例建立 它自己的单个共享会话,用于所有请求。通常,您需要使用单个 每个服务器仅实例。您可以使用stop()方法以关闭 session 显式地进行。spring-doc.cadn.net.cn

一次RSocketGraphQlTester创建后,您可以开始使用相同的 API 执行请求,独立于底层 运输。spring-doc.cadn.net.cn

ExecutionGraphQlService

很多时候,在服务器端测试 GraphQL 请求就足够了,而无需使用 客户端通过传输协议发送请求。要直接针对ExecutionGraphQlService,请使用ExecutionGraphQlServiceTester外延:spring-doc.cadn.net.cn

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

一次ExecutionGraphQlServiceTester创建后,您可以开始使用相同的 API 执行请求,独立于底层 运输。spring-doc.cadn.net.cn

ExecutionGraphQlServiceTester.Builder提供自定义选项ExecutionInput详:spring-doc.cadn.net.cn

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

WebGraphQlHandler

ExecutionGraphQlService扩展允许您在服务器端进行测试,而无需 一个客户。但是,在某些情况下,涉及服务器端传输是有用的 处理。spring-doc.cadn.net.cn

WebGraphQlTester扩展允许您通过WebGraphQlInterceptor链,然后交给ExecutionGraphQlService为 请求执行:spring-doc.cadn.net.cn

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

此扩展的生成器允许您定义 HTTP 请求详细信息:spring-doc.cadn.net.cn

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

一次WebGraphQlTester创建后,您可以开始使用相同的 API 执行请求,而与底层传输无关。spring-doc.cadn.net.cn

架构工人

GraphQlTester定义父项Builder替换为 所有扩展的构建器。它允许您配置以下内容:spring-doc.cadn.net.cn

请求

一旦你有了GraphQlTester中,您可以开始测试请求。下面执行一个 查询项目并使用 JsonPath 提取 项目发布版本:spring-doc.cadn.net.cn

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

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

JsonPath 是相对于响应的 “data” 部分的。spring-doc.cadn.net.cn

您还可以创建带有扩展名的文档文件.graphql.gql"graphql-test/"并按文件名引用它们。spring-doc.cadn.net.cn

例如,给定一个名为projectReleases.graphqlsrc/main/resources/graphql-test,内容为:spring-doc.cadn.net.cn

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

然后,您可以使用:spring-doc.cadn.net.cn

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

此方法也适用于为查询加载片段。 片段是可重用的字段选择集,可避免在请求文档中重复。 例如,我们可以使用…​releasesfragment 中:spring-doc.cadn.net.cn

src/main/resources/graphql-documents/projectReleases.graphql
query frameworkReleases {
	project(slug: "spring-framework") {
		name
		...releases
	}
}
query graphqlReleases {
       project(slug: "spring-graphql") {
           name
           ...releases
       }
   }

此片段可以在单独的文件中定义以供重用:spring-doc.cadn.net.cn

src/main/resources/graphql-documents/releases.graphql
fragment releases on Project {
   	releases {
           version
       }
   }

然后,您可以沿查询文档发送此片段:spring-doc.cadn.net.cn

graphQlTester.documentName("projectReleases") (1)
		.fragmentName("releases") (2)
		.execute()
		.path("frameworkReleases.project.releases[*].version")
		.entityList(String.class)
		.hasSizeGreaterThan(1);
1 从 “projectReleases.graphql” 加载文档
2 从 “releases.graphql” 加载片段并将其附加到文档中

IntelliJ 的“JS GraphQL”插件支持具有代码完成的 GraphQL 查询文件。spring-doc.cadn.net.cn

如果请求没有任何响应数据,例如 mutation,请使用executeAndVerify而不是execute要验证响应中没有错误,请执行以下作:spring-doc.cadn.net.cn

graphQlTester.query(query).executeAndVerify();

有关错误处理的更多详细信息,请参阅 错误spring-doc.cadn.net.cn

嵌套路径

默认情况下,路径是相对于 GraphQL 响应的 “data” 部分的。您还可以 嵌套到一个 path,并检查相对于它的多个 path,如下所示:spring-doc.cadn.net.cn

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 使用回调检查相对于 “project” 的路径。

订阅

要测试订阅,请调用executeSubscription而不是execute获取流 的响应,然后使用StepVerifier从 Project Reactor 检查流:spring-doc.cadn.net.cn

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();

错误

当您使用verify(),则响应中 “errors” 键下的任何错误都会导致 断言失败。要禁止显示特定错误,请在verify():spring-doc.cadn.net.cn

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

您可以在生成器级别注册错误过滤器,以应用于所有测试:spring-doc.cadn.net.cn

WebGraphQlTester graphQlTester = WebGraphQlTester.builder(handler)
		.errorFilter((error) -> error.getMessage().equals("ignored error"))
		.build();

如果要验证错误确实存在,并且与filter,抛出一个 assertion 错误,如果没有,则使用expect相反:spring-doc.cadn.net.cn

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

您还可以通过Consumer,这样做也会将它们标记为 filtered,因此您还可以检查响应中的数据:spring-doc.cadn.net.cn

graphQlTester.document(document)
		.execute()
		.errors()
		.satisfy((errors) ->
				assertThat(errors)
						.anyMatch((error) -> error.getMessage().contains("ignored error"))
		);