Apply Spring JavaFormat to Spring GraphQL Test

See gh-54
This commit is contained in:
Brian Clozel
2021-06-02 14:29:01 +02:00
parent d048c2e4fe
commit 39937679e3
9 changed files with 284 additions and 320 deletions

View File

@@ -2,6 +2,7 @@
plugins {
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java-library'
id "org.springframework.graphql.conventions"
}
description = "Spring Support for Testing GraphQL Applications"

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.net.URI;
@@ -61,6 +62,8 @@ import org.springframework.util.StringUtils;
/**
* Default implementation of {@link GraphQlTester}.
*
* @author Rossen Stoyanchev
*/
class DefaultGraphQlTester implements GraphQlTester {
@@ -68,16 +71,14 @@ class DefaultGraphQlTester implements GraphQlTester {
static {
ClassLoader classLoader = DefaultGraphQlTester.class.getClassLoader();
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader)
&& ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
}
private final RequestStrategy requestStrategy;
private final Configuration jsonPathConfig;
DefaultGraphQlTester(WebTestClient client) {
this.jsonPathConfig = initJsonPathConfig();
this.requestStrategy = new WebTestClientRequestStrategy(client, this.jsonPathConfig);
@@ -92,13 +93,11 @@ class DefaultGraphQlTester implements GraphQlTester {
return (jackson2Present ? Jackson2Configuration.create() : Configuration.builder().build());
}
@Override
public RequestSpec query(String query) {
return new DefaultRequestSpec(query);
}
/**
* Encapsulate how a GraphQL request is performed.
*/
@@ -106,21 +105,24 @@ class DefaultGraphQlTester implements GraphQlTester {
/**
* Perform a request with the given {@link RequestInput} container.
* @param input the request input
* @return the response spec
*/
GraphQlTester.ResponseSpec execute(RequestInput input);
/**
* Perform a subscription with the given {@link RequestInput} container.
* @param input the request input
* @return the subscription spec
*/
GraphQlTester.SubscriptionSpec executeSubscription(RequestInput input);
}
/**
* {@link RequestStrategy} that works as an HTTP client with requests
* executed through {@link WebTestClient} that in turn may work connect with
* or without a live server for Spring MVC and WebFlux.
* {@link RequestStrategy} that works as an HTTP client with requests executed through
* {@link WebTestClient} that in turn may work connect with or without a live server
* for Spring MVC and WebFlux.
*/
private static class WebTestClientRequestStrategy implements RequestStrategy {
@@ -135,14 +137,9 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public ResponseSpec execute(RequestInput requestInput) {
EntityExchangeResult<byte[]> result = this.client.post()
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestInput)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.APPLICATION_JSON)
.expectBody()
.returnResult();
EntityExchangeResult<byte[]> result = this.client.post().contentType(MediaType.APPLICATION_JSON)
.bodyValue(requestInput).exchange().expectStatus().isOk().expectHeader()
.contentType(MediaType.APPLICATION_JSON).expectBody().returnResult();
byte[] bytes = result.getResponseBodyContent();
Assert.notNull(bytes, "Expected GraphQL response content");
@@ -155,20 +152,15 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public SubscriptionSpec executeSubscription(RequestInput requestInput) {
FluxExchangeResult<TestExecutionResult> exchangeResult = this.client.post()
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.TEXT_EVENT_STREAM)
.bodyValue(requestInput)
.exchange()
.expectStatus().isOk()
.expectHeader().contentType(MediaType.TEXT_EVENT_STREAM)
.contentType(MediaType.APPLICATION_JSON).accept(MediaType.TEXT_EVENT_STREAM).bodyValue(requestInput)
.exchange().expectStatus().isOk().expectHeader().contentType(MediaType.TEXT_EVENT_STREAM)
.returnResult(TestExecutionResult.class);
return new DefaultSubscriptionSpec(
exchangeResult.getResponseBody().cast(ExecutionResult.class),
return new DefaultSubscriptionSpec(exchangeResult.getResponseBody().cast(ExecutionResult.class),
this.jsonPathConfig, exchangeResult::assertWithDiagnostics);
}
}
}
/**
* {@link RequestStrategy} that performs requests directly on {@link GraphQL}.
@@ -181,12 +173,11 @@ class DefaultGraphQlTester implements GraphQlTester {
private static final Duration DEFAULT_TIMEOUT = Duration.ofSeconds(5);
private final WebGraphQlHandler graphQlHandler;
private final Configuration jsonPathConfig;
public DirectRequestStrategy(WebGraphQlHandler handler, Configuration jsonPathConfig) {
DirectRequestStrategy(WebGraphQlHandler handler, Configuration jsonPathConfig) {
this.graphQlHandler = handler;
this.jsonPathConfig = jsonPathConfig;
}
@@ -205,8 +196,9 @@ class DefaultGraphQlTester implements GraphQlTester {
List<GraphQLError> errors = result.getErrors();
Consumer<Runnable> assertDecorator = assertDecorator(input);
assertDecorator.accept(() -> AssertionErrors.assertTrue(
"Response has " + errors.size() + " unexpected error(s).", CollectionUtils.isEmpty(errors)));
assertDecorator
.accept(() -> AssertionErrors.assertTrue("Response has " + errors.size() + " unexpected error(s).",
CollectionUtils.isEmpty(errors)));
return new DefaultSubscriptionSpec(result.getData(), this.jsonPathConfig, assertDecorator);
}
@@ -219,7 +211,7 @@ class DefaultGraphQlTester implements GraphQlTester {
}
private Consumer<Runnable> assertDecorator(RequestInput input) {
return assertion -> {
return (assertion) -> {
try {
assertion.run();
}
@@ -228,13 +220,13 @@ class DefaultGraphQlTester implements GraphQlTester {
}
};
}
}
}
/**
* {@link RequestSpec} that collects the query, operationName, and variables.
*/
private class DefaultRequestSpec implements RequestSpec {
private final class DefaultRequestSpec implements RequestSpec {
private final String query;
@@ -284,12 +276,12 @@ class DefaultGraphQlTester implements GraphQlTester {
RequestInput input = new RequestInput(this.query, this.operationName, this.variables);
return DefaultGraphQlTester.this.requestStrategy.executeSubscription(input);
}
}
}
private static class ErrorsContainer {
private static final Predicate<GraphQLError> MATCH_ALL_PREDICATE = error -> true;
private static final Predicate<GraphQLError> MATCH_ALL_PREDICATE = (error) -> true;
private final List<TestGraphQlError> errors;
@@ -302,32 +294,35 @@ class DefaultGraphQlTester implements GraphQlTester {
this.assertDecorator = assertDecorator;
}
public void doAssert(Runnable task) {
void doAssert(Runnable task) {
this.assertDecorator.accept(task);
}
public void filterErrors(Predicate<GraphQLError> errorPredicate) {
this.errors.forEach(error -> error.filter(errorPredicate));
void filterErrors(Predicate<GraphQLError> errorPredicate) {
this.errors.forEach((error) -> error.filter(errorPredicate));
}
public void consumeErrors(Consumer<List<GraphQLError>> consumer) {
void consumeErrors(Consumer<List<GraphQLError>> consumer) {
filterErrors(MATCH_ALL_PREDICATE);
consumer.accept(new ArrayList<>(this.errors));
}
public void verifyErrors() {
void verifyErrors() {
List<TestGraphQlError> unexpected =
this.errors.stream().filter(error -> !error.isExpected()).collect(Collectors.toList());
List<TestGraphQlError> unexpected = this.errors.stream().filter((error) -> !error.isExpected())
.collect(Collectors.toList());
this.assertDecorator.accept(() -> AssertionErrors.assertTrue(
"Response has " + unexpected.size() + " unexpected error(s)" +
(unexpected.size() != this.errors.size() ? " of " + this.errors.size() + " total" : "") + ". " +
"If expected, please use ResponseSpec#errors to filter them out: " + unexpected,
CollectionUtils.isEmpty(unexpected)));
this.assertDecorator
.accept(() -> AssertionErrors.assertTrue(
"Response has " + unexpected.size() + " unexpected error(s)"
+ ((unexpected.size() != this.errors.size())
? " of " + this.errors.size() + " total" : "")
+ ". " + "If expected, please use ResponseSpec#errors to filter them out: "
+ unexpected,
CollectionUtils.isEmpty(unexpected)));
}
}
}
/**
* Container for a GraphQL response with access to data and errors.
@@ -349,18 +344,19 @@ class DefaultGraphQlTester implements GraphQlTester {
private static List<TestGraphQlError> readErrors(DocumentContext documentContext) {
Assert.notNull(documentContext, "DocumentContext is required");
try {
return documentContext.read(ERRORS_PATH, new TypeRef<List<TestGraphQlError>>() {});
return documentContext.read(ERRORS_PATH, new TypeRef<List<TestGraphQlError>>() {
});
}
catch (PathNotFoundException ex) {
return Collections.emptyList();
}
}
public String jsonContent() {
String jsonContent() {
return this.jsonContent;
}
public String jsonContent(JsonPath jsonPath) {
String jsonContent(JsonPath jsonPath) {
try {
Object content = this.documentContext.read(jsonPath);
return this.documentContext.configuration().jsonProvider().toJson(content);
@@ -370,25 +366,24 @@ class DefaultGraphQlTester implements GraphQlTester {
}
}
public <T> T read(JsonPath jsonPath, TypeRef<T> typeRef) {
<T> T read(JsonPath jsonPath, TypeRef<T> typeRef) {
return this.documentContext.read(jsonPath, typeRef);
}
}
}
/**
* {@link ResponseSpec} that operates on the response from a GraphQL HTTP request.
*/
private static class DefaultResponseSpec implements ResponseSpec, ErrorSpec {
private static final class DefaultResponseSpec implements ResponseSpec, ErrorSpec {
private final ResponseContainer responseContainer;
/**
* Class constructor.
* @param documentContext the parsed response content
* @param assertDecorator decorator to apply around assertions, e.g. to
* add extra contextual information such as HTTP request and response
* body details
* @param assertDecorator decorator to apply around assertions, e.g. to add extra
* contextual information such as HTTP request and response body details
*/
private DefaultResponseSpec(DocumentContext documentContext, Consumer<Runnable> assertDecorator) {
this.responseContainer = new ResponseContainer(documentContext, assertDecorator);
@@ -422,8 +417,8 @@ class DefaultGraphQlTester implements GraphQlTester {
this.responseContainer.consumeErrors(consumer);
return this;
}
}
}
/**
* {@link PathSpec} implementation.
@@ -438,7 +433,6 @@ class DefaultGraphQlTester implements GraphQlTester {
private final JsonPathExpectationsHelper pathHelper;
DefaultPathSpec(String path, ResponseContainer responseContainer) {
Assert.notNull(path, "`path` is required");
Assert.notNull(responseContainer, "ResponseContainer is required");
@@ -458,7 +452,6 @@ class DefaultGraphQlTester implements GraphQlTester {
return JsonPath.compile(path);
}
@Override
public PathSpec path(String path) {
return new DefaultPathSpec(path, this.responseContainer);
@@ -466,29 +459,26 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public PathSpec pathExists() {
this.responseContainer.doAssert(
() -> this.pathHelper.hasJsonPath(this.responseContainer.jsonContent()));
this.responseContainer.doAssert(() -> this.pathHelper.hasJsonPath(this.responseContainer.jsonContent()));
return this;
}
@Override
public PathSpec pathDoesNotExist() {
this.responseContainer.doAssert(
() -> this.pathHelper.doesNotHaveJsonPath(this.responseContainer.jsonContent()));
this.responseContainer
.doAssert(() -> this.pathHelper.doesNotHaveJsonPath(this.responseContainer.jsonContent()));
return this;
}
@Override
public PathSpec valueExists() {
this.responseContainer.doAssert(
() -> this.pathHelper.exists(this.responseContainer.jsonContent()));
this.responseContainer.doAssert(() -> this.pathHelper.exists(this.responseContainer.jsonContent()));
return this;
}
@Override
public PathSpec valueDoesNotExist() {
this.responseContainer.doAssert(
() -> this.pathHelper.doesNotExist(this.responseContainer.jsonContent()));
this.responseContainer.doAssert(() -> this.pathHelper.doesNotExist(this.responseContainer.jsonContent()));
return this;
}
@@ -507,8 +497,8 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public PathSpec valueIsNotEmpty() {
this.responseContainer.doAssert(
() -> this.pathHelper.assertValueIsNotEmpty(this.responseContainer.jsonContent()));
this.responseContainer
.doAssert(() -> this.pathHelper.assertValueIsNotEmpty(this.responseContainer.jsonContent()));
return this;
}
@@ -555,18 +545,17 @@ class DefaultGraphQlTester implements GraphQlTester {
new JsonExpectationsHelper().assertJsonEqual(expected, actual, strict);
}
catch (AssertionError ex) {
throw new AssertionError(ex.getMessage() + "\n\n" +
"Expected JSON content:\n'" + expected + "'\n\n" +
"Actual JSON content:\n'" + actual + "'\n\n" +
"Input path: '" + this.inputPath + "'\n", ex);
throw new AssertionError(ex.getMessage() + "\n\n" + "Expected JSON content:\n'" + expected + "'\n\n"
+ "Actual JSON content:\n'" + actual + "'\n\n" + "Input path: '" + this.inputPath + "'\n",
ex);
}
catch (Exception ex) {
throw new AssertionError("JSON parsing error", ex);
}
});
}
}
}
/**
* {@link EntitySpec} implementation.
@@ -604,22 +593,19 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public <T extends S> T isEqualTo(Object expected) {
this.responseContainer.doAssert(
() -> AssertionErrors.assertEquals(this.inputPath, expected, this.entity));
this.responseContainer.doAssert(() -> AssertionErrors.assertEquals(this.inputPath, expected, this.entity));
return self();
}
@Override
public <T extends S> T isNotEqualTo(Object other) {
this.responseContainer.doAssert(
() -> AssertionErrors.assertNotEquals(this.inputPath, other, this.entity));
this.responseContainer.doAssert(() -> AssertionErrors.assertNotEquals(this.inputPath, other, this.entity));
return self();
}
@Override
public <T extends S> T isSameAs(Object expected) {
this.responseContainer.doAssert(
() -> AssertionErrors.assertTrue(this.inputPath, expected == this.entity));
this.responseContainer.doAssert(() -> AssertionErrors.assertTrue(this.inputPath, expected == this.entity));
return self();
}
@@ -631,8 +617,8 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public <T extends S> T matches(Predicate<D> predicate) {
this.responseContainer.doAssert(
() -> AssertionErrors.assertTrue(this.inputPath, predicate.test(this.entity)));
this.responseContainer
.doAssert(() -> AssertionErrors.assertTrue(this.inputPath, predicate.test(this.entity)));
return self();
}
@@ -651,8 +637,8 @@ class DefaultGraphQlTester implements GraphQlTester {
private <T extends S> T self() {
return (T) this;
}
}
}
/**
* {@link ListEntitySpec} implementation.
@@ -669,8 +655,7 @@ class DefaultGraphQlTester implements GraphQlTester {
public ListEntitySpec<E> contains(E... elements) {
doAssert(() -> {
List<E> expected = Arrays.asList(elements);
AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' does not contain " + expected,
AssertionErrors.assertTrue("List at path '" + getInputPath() + "' does not contain " + expected,
(getEntity() != null && getEntity().containsAll(expected)));
});
return this;
@@ -702,36 +687,32 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public ListEntitySpec<E> hasSize(int size) {
doAssert(() ->
AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size " + size,
(getEntity() != null && getEntity().size() == size)));
doAssert(() -> AssertionErrors.assertTrue("List at path '" + getInputPath() + "' should have size " + size,
(getEntity() != null && getEntity().size() == size)));
return this;
}
@Override
public ListEntitySpec<E> hasSizeLessThan(int boundary) {
doAssert(() ->
AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size less than " + boundary,
(getEntity() != null && getEntity().size() < boundary)));
doAssert(() -> AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size less than " + boundary,
(getEntity() != null && getEntity().size() < boundary)));
return this;
}
@Override
public ListEntitySpec<E> hasSizeGreaterThan(int boundary) {
doAssert(() ->
AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size greater than " + boundary,
(getEntity() != null && getEntity().size() > boundary)));
doAssert(() -> AssertionErrors.assertTrue(
"List at path '" + getInputPath() + "' should have size greater than " + boundary,
(getEntity() != null && getEntity().size() > boundary)));
return this;
}
}
/**
* {@link SubscriptionSpec} implementation that operates on a
* {@link Publisher} of {@link ExecutionResult}.
* {@link SubscriptionSpec} implementation that operates on a {@link Publisher} of
* {@link ExecutionResult}.
*/
private static class DefaultSubscriptionSpec implements SubscriptionSpec {
@@ -741,8 +722,8 @@ class DefaultGraphQlTester implements GraphQlTester {
private final Consumer<Runnable> assertDecorator;
<T> DefaultSubscriptionSpec(
Publisher<ExecutionResult> publisher, Configuration jsonPathConfig, Consumer<Runnable> decorator) {
<T> DefaultSubscriptionSpec(Publisher<ExecutionResult> publisher, Configuration jsonPathConfig,
Consumer<Runnable> decorator) {
this.publisher = publisher;
this.jsonPathConfig = jsonPathConfig;
@@ -751,22 +732,21 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public Flux<ResponseSpec> toFlux() {
return Flux.from(this.publisher).map(result -> {
return Flux.from(this.publisher).map((result) -> {
DocumentContext context = JsonPath.parse(result.toSpecification(), this.jsonPathConfig);
return new DefaultResponseSpec(context, this.assertDecorator);
});
}
}
}
private static class Jackson2Configuration {
static Configuration create() {
return Configuration.builder()
.jsonProvider(new JacksonJsonProvider())
.mappingProvider(new JacksonMappingProvider())
.build();
return Configuration.builder().jsonProvider(new JacksonJsonProvider())
.mappingProvider(new JacksonMappingProvider()).build();
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.util.List;
@@ -29,13 +30,12 @@ import org.springframework.lang.Nullable;
import org.springframework.test.web.reactive.server.WebTestClient;
/**
* Main entry point for testing GraphQL with requests performed either as an
* HTTP client via {@link WebTestClient} or directly via a
* {@link WebGraphQlHandler}.
* Main entry point for testing GraphQL with requests performed either as an HTTP client
* via {@link WebTestClient} or directly via a {@link WebGraphQlHandler}.
*
*
* <p>GraphQL requests to Spring MVC without an HTTP server:
* <pre class="code">
* <p>
* GraphQL requests to Spring MVC without an HTTP server: <pre class="code">
* &#064;SpringBootTest
* &#064;AutoConfigureMockMvc
* public class MyTests {
@@ -49,8 +49,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* }
* </pre>
*
* <p>GraphQL requests to Spring WebFlux without an HTTP server:
* <pre class="code">
* <p>
* GraphQL requests to Spring WebFlux without an HTTP server: <pre class="code">
* &#064;SpringBootTest
* &#064;AutoConfigureWebTestClient
* public class MyTests {
@@ -63,8 +63,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* }
* </pre>
*
* <p>GraphQL requests to a running server:
* <pre class="code">
* <p>
* GraphQL requests to a running server: <pre class="code">
* &#064;SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
* public class MyTests {
*
@@ -76,8 +76,8 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* }
* </pre>
*
* <p>GraphQL requests to any {@link WebGraphQlHandler}:
* <pre class="code">
* <p>
* GraphQL requests to any {@link WebGraphQlHandler}: <pre class="code">
* &#064;SpringBootTest
* public class MyTests {
*
@@ -88,24 +88,26 @@ import org.springframework.test.web.reactive.server.WebTestClient;
* this.graphQlTester = GraphQlTester.create(handler);
* }
* </pre>
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public interface GraphQlTester {
/**
* Prepare to perform a GraphQL request with the given operation which may
* be a query, mutation, or a subscription.
* Prepare to perform a GraphQL request with the given operation which may be a query,
* mutation, or a subscription.
* @param query the operation to be performed
* @return spec for response assertions
* @throws AssertionError if the response status is not 200 (OK)
*/
RequestSpec query(String query);
/**
* Create a {@code GraphQlTester} that performs GraphQL requests as an HTTP
* client through the given {@link WebTestClient}. Depending on how the
* {@code WebTestClient} is set up, tests may be with or without a server.
* See setup examples in class-level Javadoc.
* Create a {@code GraphQlTester} that performs GraphQL requests as an HTTP client
* through the given {@link WebTestClient}. Depending on how the {@code WebTestClient}
* is set up, tests may be with or without a server. See setup examples in class-level
* Javadoc.
* @param client the web client to perform requests with
* @return the created {@code GraphQlTester} instance
*/
@@ -114,8 +116,8 @@ public interface GraphQlTester {
}
/**
* Create a {@code GraphQlTester} that performs GraphQL requests through
* the given {@link WebGraphQlHandler}.
* Create a {@code GraphQlTester} that performs GraphQL requests through the given
* {@link WebGraphQlHandler}.
* @param handler the handler to execute requests with
* @return the created {@code GraphQlTester} instance
*/
@@ -123,19 +125,17 @@ public interface GraphQlTester {
return new DefaultGraphQlTester(handler);
}
/**
* Declare options to perform a GraphQL request.
*/
interface ExecuteSpec {
/**
* Execute the GraphQL request and return a spec for further inspection
* of response data and errors.
*
* Execute the GraphQL request and return a spec for further inspection of
* response data and errors.
* @return options for asserting the response
* @throws AssertionError if the request is performed over HTTP and the
* response status is not 200 (OK).
* @throws AssertionError if the request is performed over HTTP and the response
* status is not 200 (OK).
*/
ResponseSpec execute();
@@ -145,18 +145,16 @@ public interface GraphQlTester {
void executeAndVerify();
/**
* Execute the GraphQL request as a subscription and return a spec with
* options to transform the result stream.
*
* Execute the GraphQL request as a subscription and return a spec with options to
* transform the result stream.
* @return spec with options to transform the subscription result stream
* @throws AssertionError if the request is performed over HTTP and the
* response status is not 200 (OK).
* @throws AssertionError if the request is performed over HTTP and the response
* status is not 200 (OK).
*/
SubscriptionSpec executeSubscription();
}
/**
* Declare options to gather input for a GraphQL request and execute it.
*/
@@ -164,20 +162,27 @@ public interface GraphQlTester {
/**
* Set the operation name.
* @param name the operation name
* @return this request spec
*/
RequestSpec operationName(@Nullable String name);
/**
* Add a variable.
* @param name the variable name
* @param value the variable value
* @return this request spec
*/
RequestSpec variable(String name, Object value);
/**
* Modify variables by accessing the underlying map.
* @param variablesConsumer a callback for the map of variables
* @return this request spec
*/
RequestSpec variables(Consumer<Map<String, Object>> variablesConsumer);
}
}
/**
* Declare options to switch to different part of the GraphQL response.
@@ -185,20 +190,19 @@ public interface GraphQlTester {
interface TraverseSpec {
/**
* Switch to a path under the "data" section of the GraphQL response.
* The path can be an operation root type name, e.g. "project", or a
* nested path such as "project.name", or any
* Switch to a path under the "data" section of the GraphQL response. The path can
* be an operation root type name, e.g. "project", or a nested path such as
* "project.name", or any
* <a href="https://github.com/jayway/JsonPath">JsonPath</a>.
*
* @param path the path to switch to
* @return spec for asserting the content under the given path
* @throws AssertionError if the GraphQL response contains
* <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a>
* that have not be checked via {@link ResponseSpec#errors()}
* <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a> that have
* not be checked via {@link ResponseSpec#errors()}
*/
PathSpec path(String path);
}
}
/**
* Declare options to check the data and errors of a GraphQL response.
@@ -206,15 +210,15 @@ public interface GraphQlTester {
interface ResponseSpec extends TraverseSpec {
/**
* Return a spec to filter out or inspect errors. This must be used
* before traversing to a {@link #path(String)} if some errors are
* expected and need to be filtered out.
* Return a spec to filter out or inspect errors. This must be used before
* traversing to a {@link #path(String)} if some errors are expected and need to
* be filtered out.
* @return the error spec
*/
ErrorSpec errors();
}
/**
* Declare options available to assert data at a given path.
*/
@@ -233,8 +237,8 @@ public interface GraphQlTester {
PathSpec pathDoesNotExist();
/**
* Assert a value exists at the given path where the value is any
* {@code non-null} value, possibly an empty array or map.
* Assert a value exists at the given path where the value is any {@code non-null}
* value, possibly an empty array or map.
* @return spec to assert the converted entity with
*/
PathSpec valueExists();
@@ -246,22 +250,22 @@ public interface GraphQlTester {
PathSpec valueDoesNotExist();
/**
* Assert the value at the given path does not exist or is empty as defined
* in {@link org.springframework.util.ObjectUtils#isEmpty(Object)}.
* Assert the value at the given path does not exist or is empty as defined in
* {@link org.springframework.util.ObjectUtils#isEmpty(Object)}.
* @return spec to assert the converted entity with
* @see org.springframework.util.ObjectUtils#isEmpty(Object)
*/
PathSpec valueIsEmpty();
/**
* Assert the value at the given path is not {@link #valueIsEmpty()}
* Assert the value at the given path is not {@link #valueIsEmpty()}.
* @return spec to assert the converted entity with
*/
PathSpec valueIsNotEmpty();
/**
* Convert the data at the given path to the target type.
* @param entityType the type to convert to
* @param entityType the type to convert to
* @param <D> the target entity type
* @return spec to assert the converted entity with
*/
@@ -292,33 +296,36 @@ public interface GraphQlTester {
<D> ListEntitySpec<D> entityList(ParameterizedTypeReference<D> elementType);
/**
* Parse the JSON at the given path and the given expected JSON and assert
* that the two are "similar".
* <p>Use of this option requires the
* <a href="https://jsonassert.skyscreamer.org/">JSONassert</a> library
* on to be on the classpath.
* Parse the JSON at the given path and the given expected JSON and assert that
* the two are "similar".
* <p>
* Use of this option requires the
* <a href="https://jsonassert.skyscreamer.org/">JSONassert</a> library on to be
* on the classpath.
* @param expectedJson the expected JSON
* @return spec to specify a different path
* @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String, String)
* @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String,
* String)
*/
TraverseSpec matchesJson(String expectedJson);
/**
* Parse the JSON at the given path and the given expected JSON and assert
* that the two are "similar" so they contain the same attribute-value
* pairs regardless of formatting, along with lenient checking, e.g.
* extensible and non-strict array ordering.
* Parse the JSON at the given path and the given expected JSON and assert that
* the two are "similar" so they contain the same attribute-value pairs regardless
* of formatting, along with lenient checking, e.g. extensible and non-strict
* array ordering.
* @param expectedJson the expected JSON
* @return spec to specify a different path
* @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String, String, boolean)
* @see org.springframework.test.util.JsonExpectationsHelper#assertJsonEqual(String,
* String, boolean)
*/
TraverseSpec matchesJsonStrictly(String expectedJson);
}
/**
* Declare options available to assert data converted to an entity.
*
* @param <D> the entity type
* @param <S> the spec type, including subtypes
*/
@@ -367,21 +374,23 @@ public interface GraphQlTester {
/**
* Perform any assertions on the converted entity, e.g. via AssertJ.
* @param consumer the consumer to inspect the entity with
* @param <T> the spec type
* @return the same spec for more assertions
*/
<T extends S> T satisfies(Consumer<D> consumer);
/**
* Return the converted entity.
* @return the converter entity
*/
D get();
}
/**
* Extension of {@link EntitySpec} with options available to assert data
* converted to a List of entities.
* Extension of {@link EntitySpec} with options available to assert data converted to
* a List of entities.
*
* @param <E> the type of elements in the list
*/
interface ListEntitySpec<E> extends EntitySpec<List<E>, ListEntitySpec<E>> {
@@ -433,16 +442,15 @@ public interface GraphQlTester {
}
/**
* Declare options to filter out expected errors or inspect all errors and
* verify there are no unexpected errors.
* Declare options to filter out expected errors or inspect all errors and verify
* there are no unexpected errors.
*/
interface ErrorSpec {
/**
* Add a filter for expected errors. All errors that match the predicate
* are treated as expected and ignored on {@link #verify()} or when
* Add a filter for expected errors. All errors that match the predicate are
* treated as expected and ignored on {@link #verify()} or when
* {@link TraverseSpec#path(String) traversing} to a data path.
* @param errorPredicate the predicate to add
* @return the same spec to add more filters before {@link #verify()}
@@ -450,17 +458,16 @@ public interface GraphQlTester {
ErrorSpec filter(Predicate<GraphQLError> errorPredicate);
/**
* Verify there are either no errors or that there no unexpected errors
* that have not been {@link #filter(Predicate) filtered out}.
* Verify there are either no errors or that there no unexpected errors that have
* not been {@link #filter(Predicate) filtered out}.
* @return a spec to switch to a data path
*/
TraverseSpec verify();
/**
* Inspect <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a>
* in the response, if any. Use of this method effectively suppresses
* all errors and allows {@link TraverseSpec#path(String) traversing} to a
* data path.
* Inspect <a href="https://spec.graphql.org/June2018/#sec-Errors">errors</a> in
* the response, if any. Use of this method effectively suppresses all errors and
* allows {@link TraverseSpec#path(String) traversing} to a data path.
* @param errorsConsumer to inspect errors with
* @return a spec to switch to a data path
*/
@@ -468,30 +475,29 @@ public interface GraphQlTester {
}
/**
* Declare options available to assert a GraphQL Subscription response.
*/
interface SubscriptionSpec {
/**
* Return a {@link Flux} of entities converted from some part of the data
* in each subscription event.
* Return a {@link Flux} of entities converted from some part of the data in each
* subscription event.
* @param path a path into the data of each subscription event
* @param entityType the type to convert data to
* @param <T> the entity type
* @return a {@code Flux} of entities that can be further inspected,
* e.g. with {@code reactor.test.StepVerifier}
* @return a {@code Flux} of entities that can be further inspected, e.g. with
* {@code reactor.test.StepVerifier}
*/
default <T> Flux<T> toFlux(String path, Class<T> entityType) {
return toFlux().map(spec -> spec.path(path).entity(entityType).get());
return toFlux().map((spec) -> spec.path(path).entity(entityType).get());
}
/**
* Return a {@link Flux} of {@link ResponseSpec} instances, each
* representing an individual subscription event.
* @return a {@code Flux} of {@code ResponseSpec} instances that can be
* further inspected, e.g. with {@code reactor.test.StepVerifier}
* Return a {@link Flux} of {@link ResponseSpec} instances, each representing an
* individual subscription event.
* @return a {@code Flux} of {@code ResponseSpec} instances that can be further
* inspected, e.g. with {@code reactor.test.StepVerifier}
*/
Flux<ResponseSpec> toFlux();

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.util.ArrayList;
@@ -27,6 +28,9 @@ import graphql.GraphQLError;
/**
* {@link GraphQLError} with setters for deserialization.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public class TestExecutionResult implements ExecutionResult {
@@ -36,7 +40,6 @@ public class TestExecutionResult implements ExecutionResult {
private Map<Object, Object> extensions = Collections.emptyMap();
public void setData(Object data) {
this.data = data;
}
@@ -72,8 +75,7 @@ public class TestExecutionResult implements ExecutionResult {
@Override
public Map<String, Object> toSpecification() {
ExecutionResultImpl.Builder builder = ExecutionResultImpl.newExecutionResult()
.addErrors(this.errors)
ExecutionResultImpl.Builder builder = ExecutionResultImpl.newExecutionResult().addErrors(this.errors)
.extensions(this.extensions);
if (isDataPresent()) {
@@ -82,4 +84,5 @@ public class TestExecutionResult implements ExecutionResult {
return builder.build().toSpecification();
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.util.List;
@@ -28,6 +29,8 @@ import graphql.language.SourceLocation;
/**
* {@link GraphQLError} with setters to use for deserialization.
*
* @author Rossen Stoyanchev
*/
class TestGraphQlError implements GraphQLError {
@@ -43,9 +46,7 @@ class TestGraphQlError implements GraphQLError {
private boolean expected;
public void setMessage(String message) {
void setMessage(String message) {
this.message = message;
}
@@ -54,7 +55,7 @@ class TestGraphQlError implements GraphQLError {
return this.message;
}
public void setLocations(List<TestSourceLocation> locations) {
void setLocations(List<TestSourceLocation> locations) {
this.locations = TestSourceLocation.toSourceLocations(locations);
}
@@ -63,7 +64,7 @@ class TestGraphQlError implements GraphQLError {
return this.locations;
}
public void setErrorType(ErrorClassification errorType) {
void setErrorType(ErrorClassification errorType) {
this.errorType = errorType;
}
@@ -72,7 +73,7 @@ class TestGraphQlError implements GraphQLError {
return this.errorType;
}
public void setPath(List<Object> path) {
void setPath(List<Object> path) {
this.path = path;
}
@@ -81,7 +82,7 @@ class TestGraphQlError implements GraphQLError {
return this.path;
}
public void setExtensions(Map<String, Object> extensions) {
void setExtensions(Map<String, Object> extensions) {
this.extensions = extensions;
}
@@ -92,8 +93,9 @@ class TestGraphQlError implements GraphQLError {
/**
* Whether the error is marked as filtered out as expected.
* @return whether the error is marked as expected
*/
public boolean isExpected() {
boolean isExpected() {
return this.expected;
}
@@ -117,6 +119,7 @@ class TestGraphQlError implements GraphQLError {
/**
* Mark this error as expected if it matches the predicate.
* @param predicate the error predicate
*/
void filter(Predicate<GraphQLError> predicate) {
this.expected |= predicate.test(this);
@@ -127,8 +130,7 @@ class TestGraphQlError implements GraphQLError {
return toSpecification().toString();
}
private static class TestSourceLocation {
private static final class TestSourceLocation {
private int line;
@@ -136,35 +138,36 @@ class TestGraphQlError implements GraphQLError {
private String sourceName;
public void setLine(int line) {
void setLine(int line) {
this.line = line;
}
public int getLine() {
int getLine() {
return this.line;
}
public void setColumn(int column) {
void setColumn(int column) {
this.column = column;
}
public int getColumn() {
int getColumn() {
return this.column;
}
public void setSourceName(String sourceName) {
void setSourceName(String sourceName) {
this.sourceName = sourceName;
}
public String getSourceName() {
String getSourceName() {
return this.sourceName;
}
public static List<SourceLocation> toSourceLocations(List<TestSourceLocation> locations) {
static List<SourceLocation> toSourceLocations(List<TestSourceLocation> locations) {
return locations.stream()
.map(location -> new SourceLocation(location.line, location.column, location.sourceName))
.map((location) -> new SourceLocation(location.line, location.column, location.sourceName))
.collect(Collectors.toList());
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.lang.reflect.Type;
@@ -23,14 +24,16 @@ import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
/**
* {@link TypeRef} with a {@link #getType() type} that is given rather than
* obtained from the declared generic type information.
* {@link TypeRef} with a {@link #getType() type} that is given rather than obtained from
* the declared generic type information.
*
* @param <T> the referenced type
* @author Rossen Stoyanchev
*/
class TypeRefAdapter<T> extends TypeRef<T> {
private final Type type;
TypeRefAdapter(Class<T> clazz) {
this.type = clazz;
}
@@ -47,7 +50,6 @@ class TypeRefAdapter<T> extends TypeRef<T> {
this.type = ResolvableType.forClassWithGenerics(clazz, ResolvableType.forType(generic)).getType();
}
@Override
public Type getType() {
return this.type;

View File

@@ -13,6 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* GraphQL client testing support.
*/
@NonNullApi
@NonNullFields
package org.springframework.graphql.test.tester;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.util.Arrays;
@@ -51,8 +52,8 @@ import org.springframework.util.StringUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests for {@link GraphQlTester} parameterized to:
@@ -61,20 +62,18 @@ import static org.mockito.Mockito.when;
* <li>Use mock {@link WebGraphQlHandler} to return a preset {@link ExecutionResult}.
* </ul>
*
* <p>There is no actual handling via {@link graphql.GraphQL} in either scenario.
* The main focus is to verify {@link GraphQlTester} request preparation and
* response handling.
* <p>
* There is no actual handling via {@link graphql.GraphQL} in either scenario. The main
* focus is to verify {@link GraphQlTester} request preparation and response handling.
*/
public class GraphQlTesterTests {
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public static Stream<GraphQlTesterSetup> argumentSource() {
return Stream.of(new MockWebServerSetup(), new MockWebGraphQlHandlerSetup());
}
@ParameterizedTest
@MethodSource("argumentSource")
void pathAndValueExistsAndEmptyChecks(GraphQlTesterSetup setup) throws Exception {
@@ -88,7 +87,7 @@ public class GraphQlTesterTests {
spec.path("me.friends").valueIsEmpty();
spec.path("hero").pathDoesNotExist().valueDoesNotExist().valueIsEmpty();
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -103,13 +102,13 @@ public class GraphQlTesterTests {
spec.path("").matchesJson("{\"me\": {\"name\":\"Luke Skywalker\",\"friends\":[]}}");
spec.path("me").matchesJson("{\"name\":\"Luke Skywalker\"}");
spec.path("me").matchesJson("{\"friends\":[]}"); // lenient match with subset of fields
spec.path("me").matchesJson("{\"friends\":[]}"); // lenient match with subset of
// fields
assertThatThrownBy(() -> spec.path("me").matchesJsonStrictly("{\"friends\":[]}"))
.as("Extended fields should fail in strict mode")
.hasMessageContaining("Unexpected: name");
.as("Extended fields should fail in strict mode").hasMessageContaining("Unexpected: name");
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -126,22 +125,16 @@ public class GraphQlTesterTests {
MovieCharacter han = MovieCharacter.create("Han Solo");
AtomicReference<MovieCharacter> personRef = new AtomicReference<>();
MovieCharacter actual = spec.path("me").entity(MovieCharacter.class)
.isEqualTo(luke)
.isNotEqualTo(han)
.satisfies(personRef::set)
.matches(movieCharacter -> personRef.get().equals(movieCharacter))
.isSameAs(personRef.get())
.isNotSameAs(luke)
.get();
MovieCharacter actual = spec.path("me").entity(MovieCharacter.class).isEqualTo(luke).isNotEqualTo(han)
.satisfies(personRef::set).matches((movieCharacter) -> personRef.get().equals(movieCharacter))
.isSameAs(personRef.get()).isNotSameAs(luke).get();
assertThat(actual.getName()).isEqualTo("Luke Skywalker");
spec.path("")
.entity(new ParameterizedTypeReference<Map<String, MovieCharacter>>() {})
.isEqualTo(Collections.singletonMap("me", luke));
spec.path("").entity(new ParameterizedTypeReference<Map<String, MovieCharacter>>() {
}).isEqualTo(Collections.singletonMap("me", luke));
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -150,13 +143,8 @@ public class GraphQlTesterTests {
void entityList(GraphQlTesterSetup setup) throws Exception {
String query = "{me {name, friends}}";
setup.response("{" +
" \"me\":{" +
" \"name\":\"Luke Skywalker\"," +
" \"friends\":[{\"name\":\"Han Solo\"}, {\"name\":\"Leia Organa\"}]" +
" }" +
"}"
);
setup.response("{" + " \"me\":{" + " \"name\":\"Luke Skywalker\","
+ " \"friends\":[{\"name\":\"Han Solo\"}, {\"name\":\"Leia Organa\"}]" + " }" + "}");
GraphQlTester.ResponseSpec spec = setup.graphQlTester().query(query).execute();
@@ -164,22 +152,16 @@ public class GraphQlTesterTests {
MovieCharacter leia = MovieCharacter.create("Leia Organa");
MovieCharacter jabba = MovieCharacter.create("Jabba the Hutt");
List<MovieCharacter> actual = spec.path("me.friends").entityList(MovieCharacter.class)
.contains(han)
.containsExactly(han, leia)
.doesNotContain(jabba)
.hasSize(2)
.hasSizeGreaterThan(1)
.hasSizeLessThan(3)
List<MovieCharacter> actual = spec.path("me.friends").entityList(MovieCharacter.class).contains(han)
.containsExactly(han, leia).doesNotContain(jabba).hasSize(2).hasSizeGreaterThan(1).hasSizeLessThan(3)
.get();
assertThat(actual).containsExactly(han, leia);
spec.path("me.friends")
.entityList(new ParameterizedTypeReference<MovieCharacter>() {})
.containsExactly(han, leia);
spec.path("me.friends").entityList(new ParameterizedTypeReference<MovieCharacter>() {
}).containsExactly(han, leia);
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -187,23 +169,17 @@ public class GraphQlTesterTests {
@MethodSource("argumentSource")
void operationNameAndVariables(GraphQlTesterSetup setup) throws Exception {
String query = "query HeroNameAndFriends($episode: Episode) {" +
" hero(episode: $episode) {" +
" name" +
" }" +
"}";
String query = "query HeroNameAndFriends($episode: Episode) {" + " hero(episode: $episode) {" + " name"
+ " }" + "}";
setup.response("{\"hero\": {\"name\":\"R2-D2\"}}");
GraphQlTester.ResponseSpec spec = setup.graphQlTester().query(query)
.operationName("HeroNameAndFriends")
.variable("episode", "JEDI")
.variables(map -> map.put("foo", "bar"))
.execute();
GraphQlTester.ResponseSpec spec = setup.graphQlTester().query(query).operationName("HeroNameAndFriends")
.variable("episode", "JEDI").variables((map) -> map.put("foo", "bar")).execute();
spec.path("hero").entity(MovieCharacter.class).isEqualTo(MovieCharacter.create("R2-D2"));
setup.verifyRequest(input -> {
setup.verifyRequest((input) -> {
assertThat(input.getQuery()).contains(query);
assertThat(input.getOperationName()).isEqualTo("HeroNameAndFriends");
assertThat(input.getVariables()).hasSize(2);
@@ -223,7 +199,7 @@ public class GraphQlTesterTests {
assertThatThrownBy(() -> setup.graphQlTester().query(query).executeAndVerify())
.hasMessageContaining("Response has 1 unexpected error(s).");
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -237,7 +213,7 @@ public class GraphQlTesterTests {
assertThatThrownBy(() -> setup.graphQlTester().query(query).execute().path("me"))
.hasMessageContaining("Response has 1 unexpected error(s).");
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -246,18 +222,14 @@ public class GraphQlTesterTests {
void errorsPartiallyFiltered(GraphQlTesterSetup setup) throws Exception {
String query = "{me {name, friends}}";
setup.response(
GraphqlErrorBuilder.newError().message("some error").build(),
setup.response(GraphqlErrorBuilder.newError().message("some error").build(),
GraphqlErrorBuilder.newError().message("some other error").build());
assertThatThrownBy(() ->
setup.graphQlTester().query(query).execute()
.errors()
.filter(error -> error.getMessage().equals("some error"))
.verify())
.hasMessageContaining("Response has 1 unexpected error(s) of 2 total.");
assertThatThrownBy(() -> setup.graphQlTester().query(query).execute().errors()
.filter((error) -> error.getMessage().equals("some error")).verify())
.hasMessageContaining("Response has 1 unexpected error(s) of 2 total.");
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -266,17 +238,13 @@ public class GraphQlTesterTests {
void errorsFiltered(GraphQlTesterSetup setup) throws Exception {
String query = "{me {name, friends}}";
setup.response(
GraphqlErrorBuilder.newError().message("some error").build(),
setup.response(GraphqlErrorBuilder.newError().message("some error").build(),
GraphqlErrorBuilder.newError().message("some other error").build());
setup.graphQlTester().query(query).execute()
.errors()
.filter(error -> error.getMessage().startsWith("some "))
.verify()
.path("me").pathDoesNotExist();
setup.graphQlTester().query(query).execute().errors().filter((error) -> error.getMessage().startsWith("some "))
.verify().path("me").pathDoesNotExist();
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
@@ -285,26 +253,21 @@ public class GraphQlTesterTests {
void errorsConsumed(GraphQlTesterSetup setup) throws Exception {
String query = "{me {name, friends}}";
setup.response(GraphqlErrorBuilder.newError()
.message("Invalid query")
.location(new SourceLocation(1, 2))
.build());
setup.response(
GraphqlErrorBuilder.newError().message("Invalid query").location(new SourceLocation(1, 2)).build());
setup.graphQlTester().query(query).execute()
.errors().satisfy(errors -> {
assertThat(errors).hasSize(1);
assertThat(errors.get(0).getMessage()).isEqualTo("Invalid query");
assertThat(errors.get(0).getLocations()).hasSize(1);
assertThat(errors.get(0).getLocations().get(0).getLine()).isEqualTo(1);
assertThat(errors.get(0).getLocations().get(0).getColumn()).isEqualTo(2);
})
.path("me").pathDoesNotExist();
setup.graphQlTester().query(query).execute().errors().satisfy((errors) -> {
assertThat(errors).hasSize(1);
assertThat(errors.get(0).getMessage()).isEqualTo("Invalid query");
assertThat(errors.get(0).getLocations()).hasSize(1);
assertThat(errors.get(0).getLocations().get(0).getLine()).isEqualTo(1);
assertThat(errors.get(0).getLocations().get(0).getColumn()).isEqualTo(2);
}).path("me").pathDoesNotExist();
setup.verifyRequest(input -> assertThat(input.getQuery()).contains(query));
setup.verifyRequest((input) -> assertThat(input.getQuery()).contains(query));
setup.shutdown();
}
private interface GraphQlTesterSetup {
GraphQlTester graphQlTester();
@@ -327,7 +290,6 @@ public class GraphQlTesterTests {
}
private static class MockWebServerSetup implements GraphQlTesterSetup {
private final MockWebServer server;
@@ -356,12 +318,10 @@ public class GraphQlTesterTests {
sb.append("\"data\":").append(data);
}
if (!CollectionUtils.isEmpty(errors)) {
List<Map<String, Object>> errorSpecs = errors.stream()
.map(GraphQLError::toSpecification)
List<Map<String, Object>> errorSpecs = errors.stream().map(GraphQLError::toSpecification)
.collect(Collectors.toList());
sb.append(StringUtils.hasText(data) ? ", " : "")
.append("\"errors\":")
sb.append(StringUtils.hasText(data) ? ", " : "").append("\"errors\":")
.append(OBJECT_MAPPER.writeValueAsString(errorSpecs));
}
sb.append("}");
@@ -380,7 +340,8 @@ public class GraphQlTesterTests {
assertThat(request.getHeader(HttpHeaders.CONTENT_TYPE)).isEqualTo("application/json");
String content = request.getBody().readUtf8();
Map<String, Object> map = new ObjectMapper().readValue(content, new TypeReference<Map<String, Object>>() {});
Map<String, Object> map = new ObjectMapper().readValue(content, new TypeReference<Map<String, Object>>() {
});
WebInput webInput = new WebInput(request.getRequestUrl().uri(), new HttpHeaders(), map, null);
consumer.accept(webInput);
@@ -390,8 +351,8 @@ public class GraphQlTesterTests {
public void shutdown() throws Exception {
this.server.shutdown();
}
}
}
private static class MockWebGraphQlHandlerSetup implements GraphQlTesterSetup {
@@ -401,7 +362,7 @@ public class GraphQlTesterTests {
private final GraphQlTester graphQlTester;
public MockWebGraphQlHandlerSetup() {
MockWebGraphQlHandlerSetup() {
this.graphQlTester = GraphQlTester.create(this.handler);
}
@@ -414,14 +375,15 @@ public class GraphQlTesterTests {
public void response(@Nullable String data, List<GraphQLError> errors) throws Exception {
ExecutionResultImpl.Builder builder = new ExecutionResultImpl.Builder();
if (data != null) {
builder.data(OBJECT_MAPPER.readValue(data, new TypeReference<Map<String, Object>>() {}));
builder.data(OBJECT_MAPPER.readValue(data, new TypeReference<Map<String, Object>>() {
}));
}
if (!CollectionUtils.isEmpty(errors)) {
builder.addErrors(errors);
}
ExecutionResult result = builder.build();
WebOutput output = new WebOutput(mock(WebInput.class), result);
when(this.handler.handle(this.bodyCaptor.capture())).thenReturn(Mono.just(output));
given(this.handler.handle(this.bodyCaptor.capture())).willReturn(Mono.just(output));
}
@Override
@@ -429,6 +391,7 @@ public class GraphQlTesterTests {
WebInput webInput = this.bodyCaptor.getValue();
consumer.accept(webInput);
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import org.springframework.lang.Nullable;
@@ -46,11 +47,12 @@ public class MovieCharacter {
return false;
}
MovieCharacter movieCharacter = (MovieCharacter) other;
return (this.name != null ? this.name.equals(movieCharacter.name) : movieCharacter.name == null);
return (this.name != null) ? this.name.equals(movieCharacter.name) : movieCharacter.name == null;
}
@Override
public int hashCode() {
return (this.name != null ? this.name.hashCode() : 0);
return (this.name != null) ? this.name.hashCode() : 0;
}
}