Skip to content
This repository was archived by the owner on Dec 19, 2023. It is now read-only.

Commit fac33ab

Browse files
feat: add fluent API support for test template and response
Fluent assertions support is based on AssertJ assertion classes. Additional fluent API methods were added to GraQLTesTemplate to set and clear headers.
1 parent 88e72f1 commit fac33ab

37 files changed

+2070
-32
lines changed

graphql-spring-boot-test-autoconfigure/src/main/java/com/graphql/spring/boot/test/GraphQLTestAutoConfiguration.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
import com.fasterxml.jackson.databind.ObjectMapper;
44
import org.springframework.beans.factory.annotation.Value;
5+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
56
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
67
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
78
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
9+
import org.springframework.boot.test.web.client.TestRestTemplate;
810
import org.springframework.context.annotation.Bean;
911
import org.springframework.context.annotation.Configuration;
1012
import org.springframework.core.env.Environment;
13+
import org.springframework.core.io.ResourceLoader;
1114

1215
@Configuration
1316
@ConditionalOnWebApplication
@@ -16,8 +19,14 @@ public class GraphQLTestAutoConfiguration {
1619

1720
@Bean
1821
@ConditionalOnMissingBean
19-
public GraphQLTestTemplate graphQLTestUtils() {
20-
return new GraphQLTestTemplate();
22+
public GraphQLTestTemplate graphQLTestUtils(
23+
final ResourceLoader resourceLoader,
24+
final TestRestTemplate restTemplate,
25+
@Value("${graphql.servlet.mapping:/graphql}")
26+
final String graphqlMapping,
27+
final ObjectMapper objectMapper
28+
) {
29+
return new GraphQLTestTemplate(resourceLoader, restTemplate, graphqlMapping, objectMapper);
2130
}
2231

2332
@Bean

graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLResponse.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,13 @@
22

33
import com.fasterxml.jackson.databind.JsonNode;
44
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.graphql.spring.boot.test.assertions.GraphQLErrorListAssertion;
6+
import com.graphql.spring.boot.test.assertions.GraphQLFieldAssert;
7+
import com.graphql.spring.boot.test.assertions.NumberOfErrorsAssertion;
8+
import com.graphql.spring.boot.test.helper.GraphQLTestConstantsHelper;
59
import com.jayway.jsonpath.JsonPath;
610
import com.jayway.jsonpath.ReadContext;
11+
import org.springframework.boot.test.json.JsonContentAssert;
712
import org.springframework.http.HttpStatus;
813
import org.springframework.http.ResponseEntity;
914

@@ -30,6 +35,10 @@ public JsonNode readTree() throws IOException {
3035
return mapper.readTree(responseEntity.getBody());
3136
}
3237

38+
public Object getRaw(String path) {
39+
return get(path, Object.class);
40+
}
41+
3342
public String get(String path) {
3443
return get(path, String.class);
3544
}
@@ -58,4 +67,71 @@ public HttpStatus getStatusCode() {
5867
public ResponseEntity<String> getRawResponse() {
5968
return responseEntity;
6069
}
70+
71+
/**
72+
* Asserts that no errors are present in the response. An empty or null "errors" array also passes this test.
73+
* @return this object
74+
*/
75+
public GraphQLResponse assertThatNoErrorsArePresent() {
76+
return assertThatListOfErrors().hasNoErrors().and();
77+
}
78+
79+
/**
80+
* Returns an assertion for the number of errors in the response.
81+
* @return the assertion for the number of errors.
82+
*/
83+
public NumberOfErrorsAssertion assertThatNumberOfErrors() {
84+
return new NumberOfErrorsAssertion(this);
85+
}
86+
87+
/**
88+
* Returns an assertion for the list of errors in the response.
89+
* @return the assertion for the list of errors.
90+
*/
91+
public GraphQLErrorListAssertion assertThatListOfErrors() {
92+
return new GraphQLErrorListAssertion(this);
93+
}
94+
95+
/**
96+
* Returns an assertion for the given field(s).
97+
* @param jsonPath the JSON path of the field(s) to assert.
98+
* @return the assertion for the given field.
99+
*/
100+
public GraphQLFieldAssert assertThatField(final String jsonPath) {
101+
return new GraphQLFieldAssert(this, jsonPath);
102+
}
103+
104+
/**
105+
* Returns an assertion for the root "data" field.
106+
* @return the assertion for the $.data field.
107+
*/
108+
public GraphQLFieldAssert assertThatDataField() {
109+
return assertThatField(GraphQLTestConstantsHelper.DATA_PATH);
110+
}
111+
112+
/**
113+
* Returns an assertion for the root "extensions" field.
114+
* @return the assertion for the $.extensions field.
115+
*/
116+
public GraphQLFieldAssert assertThatExtensionsField() {
117+
return assertThatField(GraphQLTestConstantsHelper.EXTENSIONS_PATH);
118+
}
119+
120+
/**
121+
* Returns an assertion for the root "errors" field.
122+
* @return the assertion for the $.errors field.
123+
*/
124+
public GraphQLFieldAssert assertThatErrorsField() {
125+
return assertThatField(GraphQLTestConstantsHelper.ERRORS_PATH);
126+
}
127+
128+
/**
129+
* Returns an assertion for the JSON content of the response. Since the Spring Boot Framework does not provide
130+
* an abstract version of {@link JsonContentAssert} (as core AssertJ assertions do), it is not possible
131+
* chain other assertions after this one.
132+
* @return a {@link JsonContentAssert} instance for the content of the response.
133+
*/
134+
public JsonContentAssert assertThatJsonContent() {
135+
return new JsonContentAssert(null, responseEntity.getBody());
136+
}
61137
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.graphql.spring.boot.test;
2+
3+
import com.fasterxml.jackson.annotation.JsonTypeInfo;
4+
import graphql.ErrorClassification;
5+
import graphql.ErrorType;
6+
import graphql.GraphQLError;
7+
import graphql.language.SourceLocation;
8+
import lombok.AllArgsConstructor;
9+
import lombok.Builder;
10+
import lombok.Data;
11+
import lombok.NoArgsConstructor;
12+
13+
import java.util.List;
14+
import java.util.Map;
15+
16+
/**
17+
* An implementation of the {@link GraphQLError} interface for testing purposes.
18+
*/
19+
@Data
20+
@Builder
21+
@NoArgsConstructor
22+
@AllArgsConstructor
23+
public class GraphQLTestError implements GraphQLError {
24+
private String message;
25+
private List<SourceLocation> locations;
26+
@JsonTypeInfo(defaultImpl = ErrorType.class, use = JsonTypeInfo.Id.CLASS)
27+
private ErrorClassification errorType;
28+
private List<Object> path;
29+
private Map<String, Object> extensions;
30+
}

graphql-spring-boot-test/src/main/java/com/graphql/spring/boot/test/GraphQLTestTemplate.java

Lines changed: 121 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
import com.fasterxml.jackson.core.JsonProcessingException;
44
import com.fasterxml.jackson.databind.ObjectMapper;
55
import com.fasterxml.jackson.databind.node.ObjectNode;
6-
import org.springframework.beans.factory.annotation.Autowired;
6+
import lombok.Getter;
7+
import lombok.NonNull;
78
import org.springframework.beans.factory.annotation.Value;
89
import org.springframework.boot.test.web.client.TestRestTemplate;
910
import org.springframework.core.io.Resource;
@@ -12,25 +13,38 @@
1213
import org.springframework.http.HttpHeaders;
1314
import org.springframework.http.HttpMethod;
1415
import org.springframework.http.ResponseEntity;
16+
import org.springframework.lang.Nullable;
17+
import org.springframework.util.MultiValueMap;
1518
import org.springframework.util.StreamUtils;
1619

1720
import java.io.IOException;
1821
import java.io.InputStream;
22+
import java.nio.charset.Charset;
1923
import java.nio.charset.StandardCharsets;
24+
import java.util.Arrays;
2025
import java.util.List;
2126

2227
public class GraphQLTestTemplate {
2328

24-
@Autowired
25-
private ResourceLoader resourceLoader;
26-
@Autowired(required = false)
27-
private TestRestTemplate restTemplate;
28-
@Value("${graphql.servlet.mapping:/graphql}")
29-
private String graphqlMapping;
30-
@Autowired
31-
private ObjectMapper objectMapper;
32-
33-
private HttpHeaders headers = new HttpHeaders();
29+
private final ResourceLoader resourceLoader;
30+
private final TestRestTemplate restTemplate;
31+
private final String graphqlMapping;
32+
private final ObjectMapper objectMapper;
33+
@Getter
34+
private final HttpHeaders headers = new HttpHeaders();
35+
36+
public GraphQLTestTemplate(
37+
final ResourceLoader resourceLoader,
38+
final TestRestTemplate restTemplate,
39+
@Value("${graphql.servlet.mapping:/graphql}")
40+
final String graphqlMapping,
41+
final ObjectMapper objectMapper
42+
) {
43+
this.resourceLoader = resourceLoader;
44+
this.restTemplate = restTemplate;
45+
this.graphqlMapping = graphqlMapping;
46+
this.objectMapper = objectMapper;
47+
}
3448

3549
private String createJsonQuery(String graphql, ObjectNode variables)
3650
throws JsonProcessingException {
@@ -53,29 +67,121 @@ private String loadResource(Resource resource) throws IOException {
5367
}
5468

5569
/**
70+
* @deprecated use {{@link #withAdditionalHeader(String, String...)}} instead
5671
* Add an HTTP header that will be sent with each request this sends.
5772
*
5873
* @param name Name (key) of HTTP header to add.
5974
* @param value Value of HTTP header to add.
6075
*/
61-
public void addHeader(String name, String value) {
62-
headers.add(name, value);
76+
@Deprecated
77+
public void addHeader(final String name, final String value) {
78+
withAdditionalHeader(name, value);
79+
}
80+
81+
/**
82+
* Add an HTTP header that will be sent with each request this sends.
83+
*
84+
* @param name Name (key) of HTTP header to add.
85+
* @param value Value(s) of HTTP header to add.
86+
*/
87+
public GraphQLTestTemplate withAdditionalHeader(final String name, final String... value) {
88+
headers.addAll(name, Arrays.asList(value));
89+
return this;
90+
}
91+
92+
/**
93+
* Add multiple HTTP header that will be sent with each request this sends.
94+
*
95+
* @param additionalHeaders additional headers to add
96+
*/
97+
public GraphQLTestTemplate withAdditionalHeaders(final MultiValueMap<String, String> additionalHeaders) {
98+
headers.addAll(additionalHeaders);
99+
return this;
100+
}
101+
102+
/**
103+
* Adds a bearer token to the authorization header.
104+
* @param token the bearer token
105+
* @return self
106+
*/
107+
public GraphQLTestTemplate withBearerAuth(@NonNull final String token) {
108+
headers.setBearerAuth(token);
109+
return this;
110+
}
111+
112+
/**
113+
* Adds basic authentication to the authorization header.
114+
* @param username the username
115+
* @param password the password
116+
* @param charset the charset used by the credentials
117+
* @return self
118+
*/
119+
public GraphQLTestTemplate withBasicAuth(
120+
@NonNull final String username,
121+
@NonNull final String password,
122+
@Nullable final Charset charset
123+
) {
124+
headers.setBasicAuth(username, password, charset);
125+
return this;
126+
}
127+
128+
/**
129+
* Adds basic authentication to the authorization header.
130+
* @param username the username
131+
* @param password the password
132+
* @return self
133+
*/
134+
public GraphQLTestTemplate withBasicAuth(@NonNull final String username, @NonNull final String password) {
135+
headers.setBasicAuth(username, password, null);
136+
return this;
63137
}
64138

65139
/**
140+
* Adds basic authentication to the authorization header.
141+
* @param encodedCredentials the encoded credentials
142+
* @return self
143+
*/
144+
public GraphQLTestTemplate withBasicAuth(@NonNull final String encodedCredentials) {
145+
headers.setBasicAuth(encodedCredentials);
146+
return this;
147+
}
148+
149+
/**
150+
* @deprecated use {{@link #withHeaders(HttpHeaders)}} instead.
66151
* Replace any associated HTTP headers with the provided headers.
67152
*
68153
* @param newHeaders Headers to use.
69154
*/
155+
@Deprecated
70156
public void setHeaders(HttpHeaders newHeaders) {
71-
headers = newHeaders;
157+
withHeaders(newHeaders);
72158
}
73159

74160
/**
161+
* Replace any associated HTTP headers with the provided headers.
162+
*
163+
* @param newHeaders Headers to use.
164+
*/
165+
public GraphQLTestTemplate withHeaders(final HttpHeaders newHeaders) {
166+
return withClearHeaders().withAdditionalHeaders(newHeaders);
167+
}
168+
169+
/**
170+
* @deprecated use {{@link #withClearHeaders()}} instead
75171
* Clear all associated HTTP headers.
76172
*/
173+
@Deprecated
77174
public void clearHeaders() {
78-
setHeaders(new HttpHeaders());
175+
withClearHeaders();
176+
}
177+
178+
/**
179+
* Clear all associated HTTP headers.
180+
* @return self
181+
*/
182+
public GraphQLTestTemplate withClearHeaders() {
183+
headers.clear();
184+
return this;
79185
}
80186

81187
/**
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.graphql.spring.boot.test.assertions;
2+
3+
import com.graphql.spring.boot.test.GraphQLResponse;
4+
import org.assertj.core.api.AbstractBigDecimalAssert;
5+
6+
import java.math.BigDecimal;
7+
8+
public class GraphQLBigDecimalAssert extends AbstractBigDecimalAssert<GraphQLBigDecimalAssert>
9+
implements GraphQLResponseAssertion {
10+
11+
private final GraphQLResponse graphQlResponse;
12+
13+
public GraphQLBigDecimalAssert(final GraphQLResponse graphQLResponse, final BigDecimal actual) {
14+
super(actual, GraphQLBigDecimalAssert.class);
15+
this.graphQlResponse = graphQLResponse;
16+
}
17+
18+
@Override
19+
public GraphQLResponse and() {
20+
return graphQlResponse;
21+
}
22+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.graphql.spring.boot.test.assertions;
2+
3+
import com.graphql.spring.boot.test.GraphQLResponse;
4+
import org.assertj.core.api.AbstractBigIntegerAssert;
5+
6+
import java.math.BigInteger;
7+
8+
public class GraphQLBigIntegerAssert extends AbstractBigIntegerAssert<GraphQLBigIntegerAssert>
9+
implements GraphQLResponseAssertion {
10+
11+
private final GraphQLResponse graphQlResponse;
12+
13+
public GraphQLBigIntegerAssert(final GraphQLResponse graphQLResponse, final BigInteger actual) {
14+
super(actual, GraphQLBigIntegerAssert.class);
15+
this.graphQlResponse = graphQLResponse;
16+
}
17+
18+
@Override
19+
public GraphQLResponse and() {
20+
return graphQlResponse;
21+
}
22+
}

0 commit comments

Comments
 (0)