Skip to content

Commit 0c28928

Browse files
committed
Separate from expectations from response creation
This commit separates the creation of a response so that it is executed after the synchronized block inside which requests need to be matched and validated (for order and count). This allows a ResponseCreator to be slow or block if it has to. Issue: SPR-16319
1 parent 7ab4d0c commit 0c28928

File tree

6 files changed

+88
-30
lines changed

6 files changed

+88
-30
lines changed

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

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -73,19 +73,29 @@ public ResponseActions expectRequest(ExpectedCount count, RequestMatcher matcher
7373
return expectation;
7474
}
7575

76+
@SuppressWarnings("deprecation")
7677
@Override
7778
public ClientHttpResponse validateRequest(ClientHttpRequest request) throws IOException {
79+
RequestExpectation expectation = null;
7880
synchronized (this.requests) {
7981
if (this.requests.isEmpty()) {
8082
afterExpectationsDeclared();
8183
}
8284
try {
83-
return validateRequestInternal(request);
85+
// Try this first for backwards compatibility
86+
ClientHttpResponse response = validateRequestInternal(request);
87+
if (response != null) {
88+
return response;
89+
}
90+
else {
91+
expectation = matchRequest(request);
92+
}
8493
}
8594
finally {
8695
this.requests.add(request);
8796
}
8897
}
98+
return expectation.createResponse(request);
8999
}
90100

91101
/**
@@ -98,9 +108,34 @@ protected void afterExpectationsDeclared() {
98108
/**
99109
* Subclasses must implement the actual validation of the request
100110
* matching to declared expectations.
111+
* @deprecated as of 5.0.3 sub-classes should implement
112+
* {@link #matchRequest(ClientHttpRequest)} instead and return only the matched
113+
* expectation, leaving the call to create the response as a separate step
114+
* (to be invoked by this class).
101115
*/
102-
protected abstract ClientHttpResponse validateRequestInternal(ClientHttpRequest request)
103-
throws IOException;
116+
@Deprecated
117+
@Nullable
118+
protected ClientHttpResponse validateRequestInternal(ClientHttpRequest request)
119+
throws IOException {
120+
121+
return null;
122+
}
123+
124+
/**
125+
* As of 5.0.3 subclasses should implement this method instead of
126+
* {@link #validateRequestInternal(ClientHttpRequest)} in order to match the
127+
* request to an expectation, leaving the call to create the response as a separate step
128+
* (to be invoked by this class).
129+
* @param request the current request
130+
* @return the matched expectation with its request count updated via
131+
* {@link RequestExpectation#incrementAndValidate()}.
132+
* @since 5.0.3
133+
*/
134+
protected RequestExpectation matchRequest(ClientHttpRequest request) throws IOException {
135+
throw new java.lang.UnsupportedOperationException(
136+
"It looks like neither the deprecated \"validateRequestInternal\"" +
137+
"nor its replacement (this method) are implemented.");
138+
}
104139

105140
@Override
106141
public void verify() {
@@ -162,10 +197,15 @@ protected static class RequestExpectationGroup {
162197

163198
private final Set<RequestExpectation> expectations = new LinkedHashSet<>();
164199

200+
165201
public Set<RequestExpectation> getExpectations() {
166202
return this.expectations;
167203
}
168204

205+
public void addAllExpectations(Collection<RequestExpectation> expectations) {
206+
this.expectations.addAll(expectations);
207+
}
208+
169209

170210
/**
171211
* Return a matching expectation, or {@code null} if none match.
@@ -186,10 +226,15 @@ public RequestExpectation findExpectation(ClientHttpRequest request) throws IOEx
186226

187227
/**
188228
* Invoke this for an expectation that has been matched.
189-
* <p>The given expectation will either be stored if it has a remaining
190-
* count or it will be removed otherwise.
229+
* <p>The count of the given expectation is incremented, then it is
230+
* either stored if remainingCount > 0 or removed otherwise.
191231
*/
192232
public void update(RequestExpectation expectation) {
233+
expectation.incrementAndValidate();
234+
updateInternal(expectation);
235+
}
236+
237+
private void updateInternal(RequestExpectation expectation) {
193238
if (expectation.hasRemainingCount()) {
194239
this.expectations.add(expectation);
195240
}
@@ -199,11 +244,12 @@ public void update(RequestExpectation expectation) {
199244
}
200245

201246
/**
202-
* Collection variant of {@link #update(RequestExpectation)} that can
203-
* be used to insert expectations.
247+
* Add expectations to this group.
248+
* @deprecated as of 5.0.3 please use {@link #addAllExpectations(Collection)} instead.
204249
*/
250+
@Deprecated
205251
public void updateAll(Collection<RequestExpectation> expectations) {
206-
expectations.forEach(this::update);
252+
expectations.forEach(this::updateInternal);
207253
}
208254

209255
public void reset() {

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -88,11 +88,16 @@ public void match(ClientHttpRequest request) throws IOException {
8888
}
8989
}
9090

91+
/**
92+
* Note that as of 5.0.3, the creation of the response, which may block
93+
* intentionally, is separated from request count tracking, and this
94+
* method no longer increments the count transparently. Instead
95+
* {@link #incrementAndValidate()} must be invoked independently.
96+
*/
9197
@Override
9298
public ClientHttpResponse createResponse(@Nullable ClientHttpRequest request) throws IOException {
9399
ResponseCreator responseCreator = getResponseCreator();
94100
Assert.state(responseCreator != null, "createResponse() called before ResponseCreator was set");
95-
getRequestCount().incrementAndValidate();
96101
return responseCreator.createResponse(request);
97102
}
98103

@@ -101,6 +106,11 @@ public boolean hasRemainingCount() {
101106
return getRequestCount().hasRemainingCount();
102107
}
103108

109+
@Override
110+
public void incrementAndValidate() {
111+
getRequestCount().incrementAndValidate();
112+
}
113+
104114
@Override
105115
public boolean isSatisfied() {
106116
return getRequestCount().isSatisfied();

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

Lines changed: 7 additions & 1 deletion
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-2018 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.
@@ -34,6 +34,12 @@ public interface RequestExpectation extends ResponseActions, RequestMatcher, Res
3434
*/
3535
boolean hasRemainingCount();
3636

37+
/**
38+
* Increase the matched request count and check we haven't passed the max count.
39+
* @since 5.0.3
40+
*/
41+
void incrementAndValidate();
42+
3743
/**
3844
* Whether the requirements for this request expectation have been met.
3945
*/

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -20,7 +20,6 @@
2020
import java.util.Iterator;
2121

2222
import org.springframework.http.client.ClientHttpRequest;
23-
import org.springframework.http.client.ClientHttpResponse;
2423
import org.springframework.lang.Nullable;
2524
import org.springframework.util.Assert;
2625

@@ -53,7 +52,7 @@ protected void afterExpectationsDeclared() {
5352
}
5453

5554
@Override
56-
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
55+
protected RequestExpectation matchRequest(ClientHttpRequest request) throws IOException {
5756
RequestExpectation expectation = this.repeatExpectations.findExpectation(request);
5857
if (expectation == null) {
5958
if (this.expectationIterator == null || !this.expectationIterator.hasNext()) {
@@ -62,9 +61,8 @@ public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) thr
6261
expectation = this.expectationIterator.next();
6362
expectation.match(request);
6463
}
65-
ClientHttpResponse response = expectation.createResponse(request);
6664
this.repeatExpectations.update(expectation);
67-
return response;
65+
return expectation;
6866
}
6967

7068
@Override

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

Lines changed: 4 additions & 6 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-2018 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.
@@ -19,7 +19,6 @@
1919
import java.io.IOException;
2020

2121
import org.springframework.http.client.ClientHttpRequest;
22-
import org.springframework.http.client.ClientHttpResponse;
2322

2423
/**
2524
* {@code RequestExpectationManager} that matches requests to expectations
@@ -35,18 +34,17 @@ public class UnorderedRequestExpectationManager extends AbstractRequestExpectati
3534

3635
@Override
3736
protected void afterExpectationsDeclared() {
38-
this.remainingExpectations.updateAll(getExpectations());
37+
this.remainingExpectations.addAllExpectations(getExpectations());
3938
}
4039

4140
@Override
42-
public ClientHttpResponse validateRequestInternal(ClientHttpRequest request) throws IOException {
41+
public RequestExpectation matchRequest(ClientHttpRequest request) throws IOException {
4342
RequestExpectation expectation = this.remainingExpectations.findExpectation(request);
4443
if (expectation == null) {
4544
throw createUnexpectedRequestError(request);
4645
}
47-
ClientHttpResponse response = expectation.createResponse(request);
4846
this.remainingExpectations.update(expectation);
49-
return response;
47+
return expectation;
5048
}
5149

5250
@Override

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

Lines changed: 7 additions & 7 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-2018 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.
@@ -62,26 +62,26 @@ public void matchWithFailedExpectation() throws Exception {
6262
}
6363

6464
@Test
65-
public void hasRemainingCount() throws Exception {
65+
public void hasRemainingCount() {
6666
RequestExpectation expectation = new DefaultRequestExpectation(twice(), requestTo("/foo"));
6767
expectation.andRespond(withSuccess());
6868

69-
expectation.createResponse(createRequest(GET, "/foo"));
69+
expectation.incrementAndValidate();
7070
assertTrue(expectation.hasRemainingCount());
7171

72-
expectation.createResponse(createRequest(GET, "/foo"));
72+
expectation.incrementAndValidate();
7373
assertFalse(expectation.hasRemainingCount());
7474
}
7575

7676
@Test
77-
public void isSatisfied() throws Exception {
77+
public void isSatisfied() {
7878
RequestExpectation expectation = new DefaultRequestExpectation(twice(), requestTo("/foo"));
7979
expectation.andRespond(withSuccess());
8080

81-
expectation.createResponse(createRequest(GET, "/foo"));
81+
expectation.incrementAndValidate();
8282
assertFalse(expectation.isSatisfied());
8383

84-
expectation.createResponse(createRequest(GET, "/foo"));
84+
expectation.incrementAndValidate();
8585
assertTrue(expectation.isSatisfied());
8686
}
8787

0 commit comments

Comments
 (0)