Parameterize GraphQlTester.RequestSpec

This ensures that the order in which inputs are provided, i.e. whether
taken from the base RequestSpec or the Web subtype doesn't matter.

See gh-64
This commit is contained in:
Rossen Stoyanchev
2021-06-27 20:45:01 +01:00
parent 56ccaf685b
commit 5cd1e266b4
5 changed files with 96 additions and 38 deletions

View File

@@ -74,7 +74,7 @@ class DefaultGraphQlTester implements GraphQlTester {
@Override
public RequestSpec query(String query) {
public RequestSpec<?> query(String query) {
return new DefaultRequestSpec(this.requestStrategy, query);
}
@@ -216,9 +216,10 @@ class DefaultGraphQlTester implements GraphQlTester {
}
/**
* {@link RequestSpec} that collects the query, operationName, and variables.
* Assist with collecting the input for {@link GraphQlTester.RequestSpec},
* helping to avoid challenges with generics in the builder hierarchy.
*/
protected static class DefaultRequestSpec implements RequestSpec {
final static class RequestSpecDelegate {
private final RequestStrategy requestStrategy;
@@ -229,52 +230,88 @@ class DefaultGraphQlTester implements GraphQlTester {
private final Map<String, Object> variables = new LinkedHashMap<>();
protected DefaultRequestSpec(RequestStrategy requestStrategy, String query) {
protected RequestSpecDelegate(RequestStrategy requestStrategy, String query) {
Assert.notNull(requestStrategy, "RequestStrategy is required");
Assert.notNull(query, "`query` is required");
this.requestStrategy = requestStrategy;
this.query = query;
}
@Override
public RequestSpec operationName(@Nullable String name) {
public void operationName(@Nullable String name) {
this.operationName = name;
return this;
}
@Override
public RequestSpec variable(String name, Object value) {
public void variable(String name, Object value) {
this.variables.put(name, value);
}
public ResponseSpec execute() {
return execute(createRequestInput());
}
public ResponseSpec execute(RequestInput input) {
return this.requestStrategy.execute(input);
}
public void executeAndVerify() {
executeAndVerify(createRequestInput());
}
public void executeAndVerify(RequestInput input) {
ResponseSpec spec = this.requestStrategy.execute(input);
spec.path("$.errors").valueIsEmpty();
}
public SubscriptionSpec executeSubscription() {
return executeSubscription(createRequestInput());
}
public SubscriptionSpec executeSubscription(RequestInput input) {
return this.requestStrategy.executeSubscription(input);
}
public RequestInput createRequestInput() {
return new RequestInput(this.query, this.operationName, this.variables);
}
}
/**
* {@link RequestSpec} that collects the query, operationName, and variables.
*/
static class DefaultRequestSpec implements RequestSpec<DefaultRequestSpec> {
private final RequestSpecDelegate delegate;
protected DefaultRequestSpec(RequestStrategy requestStrategy, String query) {
this.delegate = new RequestSpecDelegate(requestStrategy, query);
}
@Override
public DefaultRequestSpec operationName(@Nullable String name) {
this.delegate.operationName(name);
return this;
}
@Override
public RequestSpec variables(Consumer<Map<String, Object>> variablesConsumer) {
variablesConsumer.accept(this.variables);
public DefaultRequestSpec variable(String name, Object value) {
this.delegate.variable(name, value);
return this;
}
@Override
public ResponseSpec execute() {
RequestInput input = createRequestInput();
return this.requestStrategy.execute(input);
return this.delegate.execute();
}
@Override
public void executeAndVerify() {
RequestInput input = createRequestInput();
ResponseSpec spec = this.requestStrategy.execute(input);
spec.path("$.errors").valueIsEmpty();
this.delegate.executeAndVerify();
}
@Override
public SubscriptionSpec executeSubscription() {
RequestInput input = createRequestInput();
return this.requestStrategy.executeSubscription(input);
}
protected RequestInput createRequestInput() {
return new RequestInput(this.query, this.operationName, this.variables);
return this.delegate.executeSubscription();
}
}

View File

@@ -208,14 +208,16 @@ class DefaultWebGraphQlTester extends DefaultGraphQlTester implements WebGraphQl
}
private static final class DefaultWebRequestSpec extends DefaultRequestSpec implements WebRequestSpec {
private static final class DefaultWebRequestSpec implements WebRequestSpec {
private static final URI DEFAULT_URL = URI.create("");
private final RequestSpecDelegate delegate;
private final HttpHeaders headers = new HttpHeaders();
DefaultWebRequestSpec(RequestStrategy requestStrategy, String query, @Nullable HttpHeaders headers) {
super(requestStrategy, query);
this.delegate = new RequestSpecDelegate(requestStrategy, query);
if (!CollectionUtils.isEmpty(headers)) {
this.headers.putAll(headers);
}
@@ -236,8 +238,34 @@ class DefaultWebGraphQlTester extends DefaultGraphQlTester implements WebGraphQl
}
@Override
protected RequestInput createRequestInput() {
RequestInput requestInput = super.createRequestInput();
public WebRequestSpec operationName(@Nullable String name) {
this.delegate.operationName(name);
return this;
}
@Override
public WebRequestSpec variable(String name, Object value) {
this.delegate.variable(name, value);
return this;
}
@Override
public ResponseSpec execute() {
return this.delegate.execute(createRequestInput());
}
@Override
public void executeAndVerify() {
this.delegate.executeAndVerify(createRequestInput());
}
@Override
public SubscriptionSpec executeSubscription() {
return this.delegate.executeSubscription(createRequestInput());
}
private RequestInput createRequestInput() {
RequestInput requestInput = this.delegate.createRequestInput();
return new WebInput(DEFAULT_URL, this.headers, requestInput.toMap(), null);
}

View File

@@ -137,14 +137,14 @@ public interface GraphQlTester {
/**
* Declare options to gather input for a GraphQL request and execute it.
*/
interface RequestSpec extends ExecuteSpec {
interface RequestSpec<T extends RequestSpec<T>> extends ExecuteSpec {
/**
* Set the operation name.
* @param name the operation name
* @return this request spec
*/
RequestSpec operationName(@Nullable String name);
T operationName(@Nullable String name);
/**
* Add a variable.
@@ -152,14 +152,7 @@ public interface GraphQlTester {
* @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);
T variable(String name, Object value);
}

View File

@@ -162,7 +162,7 @@ public interface WebGraphQlTester extends GraphQlTester {
* Extends {@link GraphQlTester.RequestSpec} with further input options
* applicable to Web requests.
*/
interface WebRequestSpec extends RequestSpec {
interface WebRequestSpec extends RequestSpec<WebRequestSpec> {
/**
* Add the given, single header value under the given name.

View File

@@ -180,7 +180,7 @@ public class GraphQlTesterTests {
GraphQlTester.ResponseSpec spec = this.graphQlTester.query(query)
.operationName("HeroNameAndFriends")
.variable("episode", "JEDI")
.variables((map) -> map.put("foo", "bar"))
.variable("foo", "bar")
.execute();
spec.path("hero").entity(MovieCharacter.class).isEqualTo(MovieCharacter.create("R2-D2"));