Skip to content

Commit a08290e

Browse files
committed
#192 support multiple matchers for request body
1 parent 8e1e7a0 commit a08290e

File tree

2 files changed

+71
-30
lines changed

2 files changed

+71
-30
lines changed

src/main/java/io/specto/hoverfly/junit/dsl/RequestMatcherBuilder.java

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,17 @@
2828
*/
2929
public class RequestMatcherBuilder {
3030

31-
private StubServiceBuilder invoker;
31+
private final StubServiceBuilder invoker;
3232
private final List<RequestFieldMatcher> method;
3333
private final List<RequestFieldMatcher> scheme;
3434
private final List<RequestFieldMatcher> destination;
3535
private final List<RequestFieldMatcher> path;
3636
private final Map<String, List<RequestFieldMatcher>> headers = new HashMap<>();
3737
private final Map<String, String> requiresState = new HashMap<>();
38-
private Map<String, List<RequestFieldMatcher>> query = new HashMap<>(); // default to match on empty query
39-
private List<RequestFieldMatcher> body = singletonList(newExactMatcher("")); // default to match on empty body
40-
38+
private final Map<String, List<RequestFieldMatcher>> query = new HashMap<>(); // default to match on empty query
39+
private final List<RequestFieldMatcher> body = new ArrayList<>();
40+
private boolean isAnyBody = false;
41+
private boolean isAnyQuery = false;
4142

4243
RequestMatcherBuilder(final StubServiceBuilder invoker,
4344
final StubServiceBuilder.HttpMethod method,
@@ -57,7 +58,7 @@ public class RequestMatcherBuilder {
5758
* @return the {@link RequestMatcherBuilder} for further customizations
5859
*/
5960
public RequestMatcherBuilder body(final String body) {
60-
this.body = singletonList(newExactMatcher(body));
61+
this.body.add(newExactMatcher(body));
6162
return this;
6263
}
6364

@@ -67,17 +68,17 @@ public RequestMatcherBuilder body(final String body) {
6768
* @return the {@link RequestMatcherBuilder} for further customizations
6869
*/
6970
public RequestMatcherBuilder body(HttpBodyConverter httpBodyConverter) {
70-
this.body = singletonList(newExactMatcher(httpBodyConverter.body()));
71+
this.body.add(newExactMatcher(httpBodyConverter.body()));
7172
return this;
7273
}
7374

7475
public RequestMatcherBuilder body(RequestFieldMatcher matcher) {
75-
this.body = singletonList(matcher);
76+
this.body.add(matcher);
7677
return this;
7778
}
7879

7980
public RequestMatcherBuilder anyBody() {
80-
this.body = null;
81+
this.isAnyBody = true;
8182
return this;
8283
}
8384

@@ -156,7 +157,7 @@ public RequestMatcherBuilder queryParam(final String key, final RequestFieldMatc
156157
* @return the {@link RequestMatcherBuilder} for further customizations
157158
*/
158159
public RequestMatcherBuilder anyQueryParams() {
159-
query = null;
160+
this.isAnyQuery = true;
160161
return this;
161162
}
162163

@@ -175,6 +176,12 @@ public StubServiceBuilder willReturn(final ResponseBuilder responseBuilder) {
175176

176177
public Request build() {
177178

179+
if (body.isEmpty()) {
180+
body.add(newExactMatcher("")); // default to match on empty body
181+
}
182+
183+
Map<String, List<RequestFieldMatcher>> query = isAnyQuery ? null : this.query;
184+
List<RequestFieldMatcher> body = isAnyBody ? null : this.body;
178185
return new Request(path, method, destination, scheme, query, null, body, headers, requiresState);
179186
}
180187

src/test/java/io/specto/hoverfly/ruletest/HoverflyDslMatcherTest.java

Lines changed: 55 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,35 +23,35 @@
2323
import static io.specto.hoverfly.junit.dsl.ResponseCreators.*;
2424
import static io.specto.hoverfly.junit.dsl.matchers.HoverflyMatchers.*;
2525
import static org.assertj.core.api.Assertions.assertThat;
26+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2627
import static org.assertj.core.api.Assertions.catchThrowable;
2728
import static org.springframework.http.HttpStatus.*;
2829
import static org.springframework.http.MediaType.APPLICATION_JSON;
2930
import static org.springframework.http.MediaType.APPLICATION_XML;
3031

3132
public class HoverflyDslMatcherTest {
3233

33-
private RestTemplate restTemplate = new RestTemplate();
34-
35-
private static SimpleBooking booking = new SimpleBooking(1, "London", "Hong Kong", LocalDate.now());
34+
private static final SimpleBooking BOOKING = new SimpleBooking(1, "London", "Hong Kong", LocalDate.now());
35+
private final RestTemplate restTemplate = new RestTemplate();
3636

3737
@ClassRule
3838
public static HoverflyRule hoverflyRule = HoverflyRule.inSimulationMode(dsl(
3939

4040
// Glob Matcher for url
4141
service(matches("www.*-test.com"))
4242
.get("/api/bookings/1")
43-
.willReturn(success(json(booking)))
43+
.willReturn(success(json(BOOKING)))
4444

4545
// Query matcher
4646
.get("/api/bookings")
4747
.queryParam("airline", contains("Pacific"))
4848
.queryParam("page", any())
49-
.willReturn(success(json(booking)))
49+
.willReturn(success(json(BOOKING)))
5050

5151
// Match any query params
5252
.get("/api/bookings/online")
5353
.anyQueryParams()
54-
.willReturn(success(json(booking)))
54+
.willReturn(success(json(BOOKING)))
5555

5656
// Match JSON body
5757
.put("/api/bookings/1")
@@ -60,7 +60,7 @@ public class HoverflyDslMatcherTest {
6060
.willReturn(success())
6161

6262
.put("/api/bookings/1")
63-
.body(equalsToJson(json(booking)))
63+
.body(equalsToJson(json(BOOKING)))
6464
.willReturn(success())
6565

6666
// Match partial JSON body
@@ -81,30 +81,37 @@ public class HoverflyDslMatcherTest {
8181
.willReturn(success())
8282

8383
.put("/api/bookings/1")
84-
.body(equalsToXml(xml(booking)))
84+
.body(equalsToXml(xml(BOOKING)))
8585
.willReturn(success())
8686

8787
// XmlPath Matcher
8888
.post("/api/bookings")
8989
.body(matchesXPath("/flightId"))
90-
.willReturn(created("http://localhost/api/bookings/1")),
90+
.willReturn(created("http://localhost/api/bookings/1"))
91+
92+
// Match using multiple body matchers
93+
.put("/api/bookings/1")
94+
.header("Content-Type", contains("application/json"))
95+
.body(contains("London"))
96+
.body(contains("BUSINESS"))
97+
.willReturn(success()),
9198

9299

93100
// Match any path
94101
service("www.always-success.com")
95-
.get(any())
96-
.willReturn(success()),
102+
.get(any())
103+
.willReturn(success()),
97104

98105
// Match any method
99106
service("www.booking-is-down.com")
100-
.anyMethod(startsWith("/api/bookings/"))
101-
.willReturn(serverError().body("booking is down")),
107+
.anyMethod(startsWith("/api/bookings/"))
108+
.willReturn(serverError().body("booking is down")),
102109

103110
// Match any body
104111
service("www.cloud-service.com")
105-
.post("/api/v1/containers")
106-
.body(any())
107-
.willReturn(created())
112+
.post("/api/v1/containers")
113+
.body(any())
114+
.willReturn(created())
108115

109116
)).printSimulationData();
110117

@@ -119,7 +126,7 @@ public void shouldBeAbleToQueryBookingsUsingHoverfly() {
119126

120127
// Then
121128
assertThat(response.getStatusCode()).isEqualTo(OK);
122-
assertThat(response.getBody()).isEqualTo(booking);
129+
assertThat(response.getBody()).isEqualTo(BOOKING);
123130
}
124131

125132
@Test
@@ -154,7 +161,7 @@ public void shouldQueryBookingWithAnyQueryParams() {
154161

155162
// Then
156163
assertThat(response.getStatusCode()).isEqualTo(OK);
157-
assertThat(response.getBody()).isEqualTo(booking);
164+
assertThat(response.getBody()).isEqualTo(BOOKING);
158165
}
159166

160167
@Test
@@ -214,7 +221,7 @@ public void shouldQueryBookingWithFuzzyQueryParameters() {
214221

215222
// Then
216223
assertThat(response.getStatusCode()).isEqualTo(OK);
217-
assertThat(response.getBody()).isEqualTo(booking);
224+
assertThat(response.getBody()).isEqualTo(BOOKING);
218225
}
219226

220227

@@ -237,7 +244,7 @@ public void shouldBeAbleToMatchBodyByJsonEqualityWithHttpBodyConverter() throws
237244
// Given
238245
final RequestEntity<String> bookFlightRequest = RequestEntity.put(new URI("http://www.my-test.com/api/bookings/1"))
239246
.contentType(APPLICATION_JSON)
240-
.body(HttpBodyConverter.OBJECT_MAPPER.writeValueAsString(booking));
247+
.body(HttpBodyConverter.OBJECT_MAPPER.writeValueAsString(BOOKING));
241248

242249
// When
243250
final ResponseEntity<String> bookFlightResponse = restTemplate.exchange(bookFlightRequest, String.class);
@@ -294,7 +301,7 @@ public void shouldBeAbleToMatchBodyByXmlEqualityWithHttpBodyConverter() throws E
294301
// Given
295302
final RequestEntity<String> bookFlightRequest = RequestEntity.put(new URI("http://www.my-test.com/api/bookings/1"))
296303
.contentType(APPLICATION_XML)
297-
.body(HttpBodyConverter.XML_MAPPER.writeValueAsString(booking));
304+
.body(HttpBodyConverter.XML_MAPPER.writeValueAsString(BOOKING));
298305

299306
// When
300307
final ResponseEntity<String> bookFlightResponse = restTemplate.exchange(bookFlightRequest, String.class);
@@ -332,4 +339,31 @@ public void shouldBeAbleToMatchAnyBody() throws Exception {
332339
// Then
333340
assertThat(bookFlightResponse.getStatusCode()).isEqualTo(CREATED);
334341
}
342+
343+
@Test
344+
public void shouldBeAbleToMatchUsingMultipleBodyMatchers() throws Exception {
345+
// Given
346+
final RequestEntity<String> bookFlightRequest = RequestEntity.put(new URI("http://www.my-test.com/api/bookings/1"))
347+
.contentType(APPLICATION_JSON)
348+
.body("{\"class\": \"BUSINESS\", \"destination\": \"London\"}");
349+
350+
// When
351+
final ResponseEntity<String> bookFlightResponse = restTemplate.exchange(bookFlightRequest, String.class);
352+
353+
// Then
354+
assertThat(bookFlightResponse.getStatusCode()).isEqualTo(OK);
355+
}
356+
357+
@Test
358+
public void shouldFailedIfRequestBodyNotMatchingAllConditions() throws Exception {
359+
360+
final RequestEntity<String> bookFlightRequest = RequestEntity.put(new URI("http://www.my-test.com/api/bookings/1"))
361+
.contentType(APPLICATION_JSON)
362+
.body("{\"class\": \"ECONOMY\", \"destination\": \"London\"}"); // Not matching because body does not contains 'BUSINESS'
363+
364+
assertThatThrownBy(() -> restTemplate.exchange(bookFlightRequest, String.class))
365+
.isInstanceOf(HttpServerErrorException.class)
366+
.hasMessageContaining("502 Bad Gateway");
367+
368+
}
335369
}

0 commit comments

Comments
 (0)