Skip to content

Commit 40b86ad

Browse files
committed
Update client-side REST test support
This change introduces new ways to define expectations on the body of the request including options for using XPath and JSONPath expressions, match XML content with XMLUnit, and expanded use of Hamcrest matchers. Similar options already exist for the server side. Further updates include sample tests, improved unit test coverage, comrehensive javadoc, and general polish.
1 parent e76563b commit 40b86ad

File tree

107 files changed

+3035
-867
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+3035
-867
lines changed

src/main/java/org/springframework/test/web/AssertionErrors.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
/**
1919
* JUnit independent assertion class.
20-
*
20+
*
2121
* @author Lukas Krecan
2222
* @author Arjen Poutsma
2323
*/
@@ -37,7 +37,7 @@ public static void fail(String message) {
3737

3838
/**
3939
* Fails a test with the given message passing along expected and actual values to be added to the message.
40-
*
40+
*
4141
* @param message the message
4242
* @param expected the expected value
4343
* @param actual the actual value
@@ -74,5 +74,5 @@ public static void assertEquals(String message, Object expected, Object actual)
7474
}
7575
fail(message, expected, actual);
7676
}
77-
77+
7878
}
Lines changed: 75 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011 the original author or authors.
2+
* Copyright 2011-2012 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.
@@ -29,18 +29,20 @@
2929
import org.springframework.util.Assert;
3030

3131
/**
32-
* Mock implementation of {@link ClientHttpRequest}. Implements {@link ResponseActions} to form a fluent API.
33-
*
34-
* @author Arjen Poutsma
35-
* @author Lukas Krecan
32+
* Mock implementation of {@code ClientHttpRequest} that maintains a list of
33+
* request expectations, in the form of {@link RequestMatcher}'s, as well as one
34+
* {@link ResponseCreator}. When {@link #execute()} is invoked, each request
35+
* matcher is invoked to verify the expectations. If all expectations are met,
36+
* a response is created with {@code ResponseCreator} and is then returned.
37+
*
38+
* <p>This class is also an implementation of {@link ResponseActions} to form a
39+
* fluent API for adding {@link RequestMatcher}'s and a {@code ResponseCreator}.
40+
*
3641
* @author Craig Walls
42+
* @author Rossen Stoyanchev
3743
*/
3844
public class MockClientHttpRequest implements ClientHttpRequest, ResponseActions {
3945

40-
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
41-
42-
private ResponseCreator responseCreator;
43-
4446
private URI uri;
4547

4648
private HttpMethod httpMethod;
@@ -49,65 +51,95 @@ public class MockClientHttpRequest implements ClientHttpRequest, ResponseActions
4951

5052
private ByteArrayOutputStream bodyStream = new ByteArrayOutputStream();
5153

54+
private final List<RequestMatcher> requestMatchers = new LinkedList<RequestMatcher>();
55+
56+
private ResponseCreator responseCreator;
57+
58+
59+
public MockClientHttpRequest(RequestMatcher requestMatcher) {
60+
Assert.notNull(requestMatcher, "RequestMatcher is required");
61+
this.requestMatchers.add(requestMatcher);
62+
}
63+
5264
public void setUri(URI uri) {
5365
this.uri = uri;
5466
}
5567

56-
public void setHttpMethod(HttpMethod httpMethod) {
57-
this.httpMethod = httpMethod;
68+
public URI getURI() {
69+
return this.uri;
5870
}
5971

60-
void addRequestMatcher(RequestMatcher requestMatcher) {
61-
Assert.notNull(requestMatcher, "'requestMatcher' must not be null");
62-
requestMatchers.add(requestMatcher);
72+
public void setMethod(HttpMethod httpMethod) {
73+
this.httpMethod = httpMethod;
6374
}
6475

65-
// ResponseActions implementation
76+
public HttpMethod getMethod() {
77+
return this.httpMethod;
78+
}
6679

67-
public ResponseActions andExpect(RequestMatcher requestMatcher) {
68-
addRequestMatcher(requestMatcher);
69-
return this;
80+
public HttpHeaders getHeaders() {
81+
return this.httpHeaders;
7082
}
7183

72-
public void andRespond(ResponseCreator responseCreator) {
73-
Assert.notNull(responseCreator, "'responseCreator' must not be null");
74-
this.responseCreator = responseCreator;
84+
public OutputStream getBody() throws IOException {
85+
return this.bodyStream;
7586
}
7687

77-
public HttpMethod getMethod() {
78-
return httpMethod;
88+
public String getBodyAsString() throws IOException {
89+
return this.bodyStream.toString("UTF-8");
7990
}
8091

81-
public URI getURI() {
82-
return uri;
92+
public byte[] getBodyAsByteArray() throws IOException {
93+
return this.bodyStream.toByteArray();
8394
}
8495

85-
public HttpHeaders getHeaders() {
86-
return httpHeaders;
96+
public ClientHttpResponse execute() throws IOException {
97+
98+
if (this.requestMatchers.isEmpty()) {
99+
throw new AssertionError("No request expectations to execute");
100+
}
101+
102+
if (this.responseCreator == null) {
103+
throw new AssertionError("No ResponseCreator was set up. Add it after request expectations, "
104+
+ "e.g. MockRestServiceServer.expect(requestTo(\"/foo\")).andRespond(withSuccess())");
105+
}
106+
107+
for (RequestMatcher requestMatcher : this.requestMatchers) {
108+
requestMatcher.match(this);
109+
}
110+
111+
return this.responseCreator.createResponse(this);
87112
}
88113

89-
public OutputStream getBody() throws IOException {
90-
return bodyStream;
114+
// ResponseActions implementation
115+
116+
public ResponseActions andExpect(RequestMatcher requestMatcher) {
117+
Assert.notNull(requestMatcher, "RequestMatcher is required");
118+
this.requestMatchers.add(requestMatcher);
119+
return this;
91120
}
92121

93-
public String getBodyContent() throws IOException {
94-
return bodyStream.toString("UTF-8");
122+
public void andRespond(ResponseCreator responseCreator) {
123+
Assert.notNull(responseCreator, "ResponseCreator is required");
124+
this.responseCreator = responseCreator;
95125
}
96126

97-
public ClientHttpResponse execute() throws IOException {
98-
if (!requestMatchers.isEmpty()) {
99-
for (RequestMatcher requestMatcher : requestMatchers) {
100-
requestMatcher.match(this);
101-
}
102-
} else {
103-
throw new AssertionError("Unexpected execute()");
127+
@Override
128+
public String toString() {
129+
StringBuilder sb = new StringBuilder();
130+
if (this.httpMethod != null) {
131+
sb.append(this.httpMethod);
104132
}
105-
106-
if (responseCreator != null) {
107-
return responseCreator.createResponse(this);
108-
} else {
109-
return null;
133+
if (this.uri != null) {
134+
sb.append(" ").append(this.uri);
135+
}
136+
if (!this.httpHeaders.isEmpty()) {
137+
sb.append(", headers : ").append(this.httpHeaders);
138+
}
139+
if (sb.length() == 0) {
140+
sb.append("Not yet initialized");
110141
}
142+
return sb.toString();
111143
}
112144

113145
}
Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011 the original author or authors.
2+
* Copyright 2011-2012 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.
@@ -17,58 +17,88 @@
1717

1818
import java.io.IOException;
1919
import java.net.URI;
20+
import java.util.ArrayList;
2021
import java.util.Iterator;
2122
import java.util.LinkedList;
2223
import java.util.List;
2324

2425
import org.springframework.http.HttpMethod;
26+
import org.springframework.http.client.ClientHttpRequest;
2527
import org.springframework.http.client.ClientHttpRequestFactory;
2628
import org.springframework.util.Assert;
2729

2830
/**
29-
* Mock implementation of {@link ClientHttpRequestFactory}. Contains a list of expected {@link MockClientHttpRequest}s,
30-
* and iterates over those.
31-
*
32-
* @author Arjen Poutsma
33-
* @author Lukas Krecan
31+
* Mock implementation of {@code ClientHttpRequestFactory} that maintains a list
32+
* of expected requests and returns each expected request whenever
33+
* {@link #createRequest(URI, HttpMethod)} is called.
34+
*
3435
* @author Craig Walls
36+
* @author Rossen Stoyanchev
3537
*/
3638
public class MockClientHttpRequestFactory implements ClientHttpRequestFactory {
3739

38-
private final List<MockClientHttpRequest> expectedRequests = new LinkedList<MockClientHttpRequest>();
40+
private final List<MockClientHttpRequest> expected = new LinkedList<MockClientHttpRequest>();
41+
42+
private final List<MockClientHttpRequest> executed = new ArrayList<MockClientHttpRequest>();
43+
44+
private Iterator<MockClientHttpRequest> iterator;
3945

40-
private Iterator<MockClientHttpRequest> requestIterator;
4146

42-
public MockClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
47+
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
4348
Assert.notNull(uri, "'uri' must not be null");
4449
Assert.notNull(httpMethod, "'httpMethod' must not be null");
4550

46-
if (requestIterator == null) {
47-
requestIterator = expectedRequests.iterator();
51+
initializeIterator();
52+
53+
MockClientHttpRequest request = this.iterator.next();
54+
request.setUri(uri);
55+
request.setMethod(httpMethod);
56+
57+
this.executed.add(request);
58+
59+
return request;
60+
}
61+
62+
private void initializeIterator() throws AssertionError {
63+
if (this.iterator == null) {
64+
this.iterator = this.expected.iterator();
4865
}
49-
if (!requestIterator.hasNext()) {
66+
if (!this.iterator.hasNext()) {
5067
throw new AssertionError("No further requests expected");
5168
}
52-
53-
MockClientHttpRequest currentRequest = requestIterator.next();
54-
currentRequest.setUri(uri);
55-
currentRequest.setHttpMethod(httpMethod);
56-
return currentRequest;
5769
}
5870

59-
MockClientHttpRequest expectNewRequest() {
60-
Assert.state(requestIterator == null, "Can not expect another request, the test is already underway");
61-
MockClientHttpRequest request = new MockClientHttpRequest();
62-
expectedRequests.add(request);
71+
MockClientHttpRequest expectRequest(RequestMatcher requestMatcher) {
72+
Assert.state(this.iterator == null, "Can't add more expectations when test is already underway");
73+
MockClientHttpRequest request = new MockClientHttpRequest(requestMatcher);
74+
this.expected.add(request);
6375
return request;
6476
}
6577

6678
void verifyRequests() {
67-
if (expectedRequests.isEmpty()) {
79+
if (this.expected.isEmpty() || this.expected.equals(this.executed)) {
6880
return;
6981
}
70-
if (requestIterator == null || requestIterator.hasNext()) {
71-
throw new AssertionError("Further request(s) expected");
82+
throw new AssertionError(getVerifyMessage());
83+
}
84+
85+
private String getVerifyMessage() {
86+
StringBuilder sb = new StringBuilder("Further request(s) expected\n");
87+
88+
if (this.executed.size() > 0) {
89+
sb.append("The following ");
90+
}
91+
sb.append(this.executed.size()).append(" out of ");
92+
sb.append(this.expected.size()).append(" were executed");
93+
94+
if (this.executed.size() > 0) {
95+
sb.append(":\n");
96+
for (MockClientHttpRequest request : this.executed) {
97+
sb.append(request.toString()).append("\n");
98+
}
7299
}
100+
101+
return sb.toString();
73102
}
103+
74104
}

src/main/java/org/springframework/test/web/client/MockHttpRequest.java

Lines changed: 0 additions & 58 deletions
This file was deleted.

0 commit comments

Comments
 (0)