テスト
Spring for GraphQL は、HTTP、WebSocket、RSocket を介した GraphQL リクエストのテスト、およびサーバーに対する直接のテストの専用サポートを提供します。
これを利用するには、ビルドに spring-graphql-test
を追加します。
dependencies {
// ...
testImplementation 'org.springframework.graphql:spring-graphql-test:1.3.2'
}
<dependencies>
<!-- ... -->
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<version>1.3.2</version>
<scope>test</scope>
</dependency>
</dependencies>
GraphQlTester
GraphQlTester
は、基礎となるトランスポートから独立した GraphQL リクエストをテストするための共通のワークフローを宣言する契約です。つまり、基礎となるトランスポートに関係なく、リクエストは同じ API でテストされ、トランスポート固有のものはビルド時に構成されます。
クライアント経由でリクエストを実行する GraphQlTester
を作成するには、次の拡張のいずれかが必要です。
クライアントなしでサーバー側でテストを実行する GraphQlTester
を作成するには:
それぞれが、トランスポートに関連するオプションを持つ Builder
を定義します。すべてのビルダーは、共通のベース GraphQlTester Builder
から拡張され、すべての拡張に関連するオプションがあります。
HTTP
HttpGraphQlTester
は WebTestClient を使用して、WebTestClient
の構成方法に応じて、ライブサーバーの有無にかかわらず、HTTP 経由で GraphQL リクエストを実行します。
ライブサーバーなしで 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 を使用してリクエストの実行を開始できます。トランスポート固有の詳細を変更する必要がある場合は、既存の 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
は接続指向で多重化されています。各インスタンスは、すべてのリクエストに対して独自の単一の共有接続を確立します。通常、サーバーごとに 1 つのインスタンスのみを使用します。
WebSocketGraphQlTester
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。トランスポート固有の詳細を変更する必要がある場合は、既存の 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
は、WebSocket 接続を閉じるために使用できる stop()
メソッドを提供します。テストの実行後。
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
は接続指向で多重化されています。各インスタンスは、すべてのリクエストに対して独自の単一の共有セッションを確立します。通常、サーバーごとに 1 つのインスタンスのみを使用します。テスターで stop()
メソッドを使用して、セッションを明示的に閉じることができます。
RSocketGraphQlTester
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。
ExecutionGraphQlService
多くの場合、クライアントを使用してトランスポートプロトコル経由でリクエストを送信することなく、サーバー側で GraphQL リクエストをテストするだけで十分です。ExecutionGraphQlService
に対して直接テストするには、ExecutionGraphQlServiceTester
拡張機能を使用します。
ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.create(service);
ExecutionGraphQlServiceTester
が作成されると、基礎となるトランスポートとは関係なく、同じ API を使用してリクエストの実行を開始できます。
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 を使用してリクエストの実行を開始できます。
要求
GraphQlTester
を取得したら、リクエストのテストを開始できます。以下は、プロジェクトのクエリを実行し、JsonPath [GitHub] (英語) を使用して、レスポンスからプロジェクトリリースバージョンを抽出します。
String document = "{" +
" project(slug:\"spring-framework\") {" +
" releases {" +
" version" +
" }"+
" }" +
"}";
graphQlTester.document(document)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
JsonPath は、レスポンスの「データ」セクションに関連しています。
クラスパス上の "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 | "project" という名前のファイル内のドキュメントを参照してください。 |
2 | slug 変数を設定します。 |
IntelliJ の "JS GraphQL" プラグインは、コード補完 で GraphQL クエリファイルをサポートします。 |
リクエストにレスポンスデータがない場合。レスポンスにエラーがないことを確認するには、execute
の代わりに executeAndVerify
を使用します。
graphQlTester.query(query).executeAndVerify();
エラー処理の詳細については、エラーを参照してください。
入れ子になったパス
デフォルトでは、パスは GraphQL レスポンスの「データ」セクションに対する相対パスです。次のように、パスまでネストして、そのパスに関連する複数のパスをインスペクションすることもできます。
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 | コールバックを使用して、「プロジェクト」に関連するパスをインスペクションします。 |
サブスクリプション
サブスクリプションをテストするには、execute
の代わりに executeSubscription
を呼び出してレスポンスのストリームを取得し、プロジェクト 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、またはサーバー側の ExecutionGraphQlService
および WebGraphQlHandler
拡張機能でのみサポートされています。
エラー
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 -> {
// ...
});