From 30fab803831bbfa3babbaead65cee6530df1c3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20B=C3=A9gaudeau?= Date: Wed, 21 Aug 2024 10:13:05 +0200 Subject: [PATCH] [doc] Add some documentation on testing custom datafetchers with subscriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Stéphane Bégaudeau --- .../test-sirius-web-based-applications.adoc | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 doc/how-to/test-sirius-web-based-applications.adoc diff --git a/doc/how-to/test-sirius-web-based-applications.adoc b/doc/how-to/test-sirius-web-based-applications.adoc new file mode 100644 index 0000000000..e79561c530 --- /dev/null +++ b/doc/how-to/test-sirius-web-based-applications.adoc @@ -0,0 +1,72 @@ += How to test Sirius Web based applications + +== How to perform GraphQL requests? + +There are three main APIs to perform GraphQL requests. + +The first way to perform a GraphQL request is by using one of the implementations of `IQueryRunner` to execute queries used to retrieves data from the backend. +It is also possible to use one of the implementations of `IMutationRunner` to perform changes in the backend. +Finally, we can use an implementation of `ISubscriptionRunner` to subscribe to flux of data to be warned when something has been changed. + +Those runners exist in order to give contributors of the Sirius Web project and downstream projects of Sirius Web a set of APIs encapsulating the body of the requests and the way to execute it. + + +== How to test custom datafetchers in a subscription + +In the project `sirius-web`, we have countless tests using subscriptions. +In order to subscribe to the flux of data of those subscription, we are most of the time relying on `IGraphQLRequestor#subscribe` directly or not by using a `XxxSubscriptionRunner` which encapsulates the body of the request too. +By using `IGraphQLRequestor#subscribe`, we will return the raw `Flux` created by the event processor. +The subscription to this flux will not trigger the execution of child data fetchers. + +For example, let's consider the following fake subscription: + +``` +subscription someRepresentationEvent($input: SomeRepresentationEventInput!) { + someRepresentationEvent(input: $input) { + __typename + } +} +``` + +Using such a subscription with `IGraphQLRequestor#subscribe` would not trigger the execution of child datafetchers. +But contrary to what one may believe, executing this subscription instead would not change this fact: + +``` +subscription someRepresentationEvent($input: SomeRepresentationEventInput!) { + someRepresentationEvent(input: $input) { + __typename + ... on SomeRepresentationRefreshedEventPayload { + someValue { + customDataFetcherWithSomeParameters(arg1: 'foo', arg2: 'bar') + } + } + } +} +``` + +By using `IGraphQLRequestor#subscribe`, the custom datafetcher will not be executed and only the raw payloads will be returned. + +In order to trigger the execution of the custom datafetcher in a subscription, one has to use `IGraphQLRequestor#subscribeToSpecification` which will instead return a `Flux` just like the one used by the frontend. +This flux will emit the JSON serialization of the data created accordingly to the official GraphQL specification. +This will trigger the execution of all the datafetchers used in the body of the request. + +In order to perform evaluation on such result, it is best to use `JsonPath` in this manner: + +``` +var input = new SomeRepresentationEventInput(...); +var flux = this.graphQLRequestor.subscribeToSpecification(SUBSCRIPTION, input); + +Consumer consumer = payload -> Optional.of(payload) + .ifPresentOrElse(body -> { + String typename = JsonPath.read(body, "$.data.someRepresentationEvent.__typename"); + assertThat(typename).isEqualTo("SomeRepresentationRefreshedEventPayload"); + + String value = JsonPath.read(body, "$.data.someRepresentationEvent.someValue.customDataFetcherWithSomeParameters"); + assertThat(value).isEqualTo("foobar"); + }, () -> fail("Missing data")); + +StepVerifier.create(flux) + .consumeNextWith(consumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); +``` \ No newline at end of file