Skip to content

Commit a88c47a

Browse files
committed
Properly handle follow-up request after failure
Issue: SPR-16132
1 parent 6dc7346 commit a88c47a

File tree

4 files changed

+106
-43
lines changed

4 files changed

+106
-43
lines changed

spring-test/src/main/java/org/springframework/test/web/client/AbstractRequestExpectationManager.java

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,24 +72,28 @@ public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOEx
7272
if (requests.isEmpty()) {
7373
afterExpectationsDeclared();
7474
}
75-
ClientHttpResponse response = validateRequestInternal(request);
76-
requests.add(request);
77-
return response;
75+
try {
76+
return validateRequestInternal(request);
77+
}
78+
finally {
79+
requests.add(request);
80+
}
7881
}
7982
}
8083

8184
/**
82-
* Invoked after the phase of declaring expected requests is over. This is
83-
* detected from {@link #validateRequest} on the first actual request.
85+
* Invoked at the time of the first actual request, which effectively means
86+
* the expectations declaration phase is over.
8487
*/
8588
protected void afterExpectationsDeclared() {
8689
}
8790

8891
/**
8992
* Subclasses must implement the actual validation of the request
90-
* matching it to a declared expectation.
93+
* matching to declared expectations.
9194
*/
92-
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException;
95+
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request)
96+
throws IOException;
9397

9498
@Override
9599
public void verify() {
@@ -145,9 +149,7 @@ public void reset() {
145149

146150

147151
/**
148-
* Helper class to manage a group of request expectations. It helps with
149-
* operations against the entire group such as finding a match and updating
150-
* (add or remove) based on expected request count.
152+
* Helper class to manage a group of remaining expectations.
151153
*/
152154
protected static class RequestExpectationGroup {
153155

@@ -157,6 +159,24 @@ public Set<RequestExpectation> getExpectations() {
157159
return this.expectations;
158160
}
159161

162+
public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException {
163+
for (RequestExpectation expectation : getExpectations()) {
164+
try {
165+
expectation.match(request);
166+
return expectation;
167+
}
168+
catch (AssertionError error) {
169+
// Ignore
170+
}
171+
}
172+
return null;
173+
}
174+
175+
/**
176+
* Invoke this for an expectation that has been matched.
177+
* <p>The given expectation will either be stored if it has a remaining
178+
* count or it will be removed otherwise.
179+
*/
160180
public void update(RequestExpectation expectation) {
161181
if (expectation.hasRemainingCount()) {
162182
getExpectations().add(expectation);
@@ -166,25 +186,16 @@ public void update(RequestExpectation expectation) {
166186
}
167187
}
168188

189+
/**
190+
* Collection variant of {@link #update(RequestExpectation)} that can
191+
* be used to insert expectations.
192+
*/
169193
public void updateAll(Collection<RequestExpectation> expectations) {
170194
for (RequestExpectation expectation : expectations) {
171195
update(expectation);
172196
}
173197
}
174198

175-
public RequestExpectation findExpectation(ClientHttpRequest request) throws IOException {
176-
for (RequestExpectation expectation : getExpectations()) {
177-
try {
178-
expectation.match(request);
179-
return expectation;
180-
}
181-
catch (AssertionError error) {
182-
// Ignore
183-
}
184-
}
185-
return null;
186-
}
187-
188199
public void reset() {
189200
getExpectations().clear();
190201
}

spring-test/src/test/java/org/springframework/test/web/client/MockRestServiceServerTests.java

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,27 +13,32 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.test.web.client;
1718

19+
import java.net.SocketException;
20+
1821
import org.junit.Test;
1922

2023
import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder;
2124
import org.springframework.web.client.RestTemplate;
2225

23-
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
24-
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
26+
import static org.springframework.http.HttpMethod.*;
27+
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
28+
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
2529

2630
/**
2731
* Unit tests for {@link MockRestServiceServer}.
32+
*
2833
* @author Rossen Stoyanchev
2934
*/
3035
public class MockRestServiceServerTests {
3136

32-
private RestTemplate restTemplate = new RestTemplate();
37+
private final RestTemplate restTemplate = new RestTemplate();
3338

3439

3540
@Test
36-
public void buildMultipleTimes() throws Exception {
41+
public void buildMultipleTimes() {
3742
MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(this.restTemplate);
3843

3944
MockRestServiceServer server = builder.build();
@@ -55,7 +60,7 @@ public void buildMultipleTimes() throws Exception {
5560
}
5661

5762
@Test(expected = AssertionError.class)
58-
public void exactExpectOrder() throws Exception {
63+
public void exactExpectOrder() {
5964
MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
6065
.ignoreExpectOrder(false).build();
6166

@@ -65,7 +70,7 @@ public void exactExpectOrder() throws Exception {
6570
}
6671

6772
@Test
68-
public void ignoreExpectOrder() throws Exception {
73+
public void ignoreExpectOrder() {
6974
MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
7075
.ignoreExpectOrder(true).build();
7176

@@ -77,7 +82,7 @@ public void ignoreExpectOrder() throws Exception {
7782
}
7883

7984
@Test
80-
public void resetAndReuseServer() throws Exception {
85+
public void resetAndReuseServer() {
8186
MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate).build();
8287

8388
server.expect(requestTo("/foo")).andRespond(withSuccess());
@@ -91,7 +96,7 @@ public void resetAndReuseServer() throws Exception {
9196
}
9297

9398
@Test
94-
public void resetAndReuseServerWithUnorderedExpectationManager() throws Exception {
99+
public void resetAndReuseServerWithUnorderedExpectationManager() {
95100
MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate)
96101
.ignoreExpectOrder(true).build();
97102

@@ -107,4 +112,24 @@ public void resetAndReuseServerWithUnorderedExpectationManager() throws Exceptio
107112
server.verify();
108113
}
109114

115+
@Test // SPR-16132
116+
public void followUpRequestAfterFailure() {
117+
MockRestServiceServer server = MockRestServiceServer.bindTo(this.restTemplate).build();
118+
119+
server.expect(requestTo("/some-service/some-endpoint"))
120+
.andRespond(request -> { throw new SocketException("pseudo network error"); });
121+
122+
server.expect(requestTo("/reporting-service/report-error"))
123+
.andExpect(method(POST)).andRespond(withSuccess());
124+
125+
try {
126+
this.restTemplate.getForEntity("/some-service/some-endpoint", String.class);
127+
}
128+
catch (Exception ex) {
129+
this.restTemplate.postForEntity("/reporting-service/report-error", ex.toString(), String.class);
130+
}
131+
132+
server.verify();
133+
}
134+
110135
}

spring-test/src/test/java/org/springframework/test/web/client/SimpleRequestExpectationManagerTests.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.test.web.client;
1818

19+
import java.net.SocketException;
1920
import java.net.URI;
2021
import java.net.URISyntaxException;
2122

@@ -168,8 +169,36 @@ public void sequentialRequestsWithDifferentCount() throws Exception {
168169
this.manager.validateRequest(createRequest(GET, "/bar"));
169170
}
170171

172+
@Test // SPR-15719
173+
public void repeatedRequestsInSequentialOrder() throws Exception {
174+
this.manager.expectRequest(times(2), requestTo("/foo")).andExpect(method(GET)).andRespond(withSuccess());
175+
this.manager.expectRequest(times(2), requestTo("/bar")).andExpect(method(GET)).andRespond(withSuccess());
176+
177+
this.manager.validateRequest(createRequest(GET, "/foo"));
178+
this.manager.validateRequest(createRequest(GET, "/foo"));
179+
this.manager.validateRequest(createRequest(GET, "/bar"));
180+
this.manager.validateRequest(createRequest(GET, "/bar"));
181+
}
182+
183+
@Test // SPR-16132
184+
public void sequentialRequestsWithFirstFailing() throws Exception {
185+
this.manager.expectRequest(once(), requestTo("/foo")).
186+
andExpect(method(GET)).andRespond(request -> { throw new SocketException("pseudo network error"); });
187+
this.manager.expectRequest(once(), requestTo("/handle-error")).
188+
andExpect(method(POST)).andRespond(withSuccess());
189+
190+
try {
191+
this.manager.validateRequest(createRequest(GET, "/foo"));
192+
fail("Expected SocketException");
193+
}
194+
catch (SocketException ex) {
195+
// expected
196+
}
197+
this.manager.validateRequest(createRequest(POST, "/handle-error"));
198+
this.manager.verify();
199+
}
200+
171201

172-
@SuppressWarnings("deprecation")
173202
private ClientHttpRequest createRequest(HttpMethod method, String url) {
174203
try {
175204
return new MockClientHttpRequest(method, new URI(url));

spring-test/src/test/java/org/springframework/test/web/client/UnorderedRequestExpectationManagerTests.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.test.web.client;
1718

1819
import java.net.URI;
@@ -26,18 +27,15 @@
2627
import org.springframework.http.client.ClientHttpRequest;
2728
import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
2829

29-
import static org.junit.Assert.assertEquals;
30-
import static org.springframework.http.HttpMethod.GET;
31-
import static org.springframework.test.web.client.ExpectedCount.max;
32-
import static org.springframework.test.web.client.ExpectedCount.min;
33-
import static org.springframework.test.web.client.ExpectedCount.once;
34-
import static org.springframework.test.web.client.ExpectedCount.times;
35-
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
36-
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
37-
import static org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess;
30+
import static org.junit.Assert.*;
31+
import static org.springframework.http.HttpMethod.*;
32+
import static org.springframework.test.web.client.ExpectedCount.*;
33+
import static org.springframework.test.web.client.match.MockRestRequestMatchers.*;
34+
import static org.springframework.test.web.client.response.MockRestResponseCreators.*;
3835

3936
/**
4037
* Unit tests for {@link UnorderedRequestExpectationManager}.
38+
*
4139
* @author Rossen Stoyanchev
4240
*/
4341
public class UnorderedRequestExpectationManagerTests {

0 commit comments

Comments
 (0)