Polish client-side REST updates

Issue: SPR-11365
This commit is contained in:
Rossen Stoyanchev 2016-02-24 15:23:30 -05:00
parent ca19920d74
commit 1bc1df2d0f
4 changed files with 73 additions and 37 deletions

View File

@ -26,8 +26,6 @@ import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.client.ClientHttpResponse;
import org.springframework.mock.http.client.MockAsyncClientHttpRequest; import org.springframework.mock.http.client.MockAsyncClientHttpRequest;
import org.springframework.test.web.client.match.MockRestRequestMatchers;
import org.springframework.test.web.client.response.MockRestResponseCreators;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.web.client.AsyncRestTemplate; import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -46,7 +44,7 @@ import org.springframework.web.client.support.RestGatewaySupport;
* *
* <pre class="code"> * <pre class="code">
* RestTemplate restTemplate = new RestTemplate() * RestTemplate restTemplate = new RestTemplate()
* MockRestServiceServer server = MockRestServiceServer.restTemplate(restTemplate).build(); * MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build();
* *
* server.expect(manyTimes(), requestTo("/hotels/42")).andExpect(method(HttpMethod.GET)) * server.expect(manyTimes(), requestTo("/hotels/42")).andExpect(method(HttpMethod.GET))
* .andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON)); * .andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON));
@ -125,56 +123,59 @@ public class MockRestServiceServer {
/** /**
* Build a {@code MockRestServiceServer} for a {@code RestTemplate}. * Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code RestTemplate}.
* @since 4.3 * @since 4.3
*/ */
public static MockRestServiceServerBuilder restTemplate(RestTemplate restTemplate) { public static MockRestServiceServerBuilder bindTo(RestTemplate restTemplate) {
return new DefaultBuilder(restTemplate); return new DefaultBuilder(restTemplate);
} }
/** /**
* Build a {@code MockRestServiceServer} for an {@code AsyncRestTemplate}. * Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code AsyncRestTemplate}.
* @since 4.3 * @since 4.3
*/ */
public static MockRestServiceServerBuilder asyncRestTemplate(AsyncRestTemplate asyncRestTemplate) { public static MockRestServiceServerBuilder bindTo(AsyncRestTemplate asyncRestTemplate) {
return new DefaultBuilder(asyncRestTemplate); return new DefaultBuilder(asyncRestTemplate);
} }
/** /**
* Build a {@code MockRestServiceServer} for a {@code RestGateway}. * Return a builder for a {@code MockRestServiceServer} that should be used
* to reply to the given {@code RestGatewaySupport}.
* @since 4.3 * @since 4.3
*/ */
public static MockRestServiceServerBuilder restGateway(RestGatewaySupport restGateway) { public static MockRestServiceServerBuilder bindTo(RestGatewaySupport restGateway) {
Assert.notNull(restGateway, "'gatewaySupport' must not be null"); Assert.notNull(restGateway, "'gatewaySupport' must not be null");
return new DefaultBuilder(restGateway.getRestTemplate()); return new DefaultBuilder(restGateway.getRestTemplate());
} }
/** /**
* A shortcut for {@code restTemplate(restTemplate).build()}. * A shortcut for {@code bindTo(restTemplate).build()}.
* @param restTemplate the RestTemplate to set up for mock testing * @param restTemplate the RestTemplate to set up for mock testing
* @return the mock server * @return the mock server
*/ */
public static MockRestServiceServer createServer(RestTemplate restTemplate) { public static MockRestServiceServer createServer(RestTemplate restTemplate) {
return restTemplate(restTemplate).build(); return bindTo(restTemplate).build();
} }
/** /**
* A shortcut for {@code asyncRestTemplate(asyncRestTemplate).build()}. * A shortcut for {@code bindTo(asyncRestTemplate).build()}.
* @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing * @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing
* @return the created mock server * @return the created mock server
*/ */
public static MockRestServiceServer createServer(AsyncRestTemplate asyncRestTemplate) { public static MockRestServiceServer createServer(AsyncRestTemplate asyncRestTemplate) {
return asyncRestTemplate(asyncRestTemplate).build(); return bindTo(asyncRestTemplate).build();
} }
/** /**
* A shortcut for {@code restGateway(restGateway).build()}. * A shortcut for {@code bindTo(restGateway).build()}.
* @param restGateway the REST gateway to set up for mock testing * @param restGateway the REST gateway to set up for mock testing
* @return the created mock server * @return the created mock server
*/ */
public static MockRestServiceServer createServer(RestGatewaySupport restGateway) { public static MockRestServiceServer createServer(RestGatewaySupport restGateway) {
return restGateway(restGateway).build(); return bindTo(restGateway).build();
} }
@ -189,13 +190,13 @@ public class MockRestServiceServer {
* matching the order of declaration. This is a shortcut for:<br> * matching the order of declaration. This is a shortcut for:<br>
* {@code builder.expectationManager(new UnorderedRequestExpectationManager)} * {@code builder.expectationManager(new UnorderedRequestExpectationManager)}
*/ */
MockRestServiceServerBuilder unordered(); MockRestServiceServerBuilder ignoreExpectOrder();
/** /**
* Configure a custom {@code RequestExpectationManager}. * Configure a custom {@code RequestExpectationManager}.
* <p>By default {@link SimpleRequestExpectationManager} is used. It is * <p>By default {@link SimpleRequestExpectationManager} is used. It is
* also possible to switch to {@link UnorderedRequestExpectationManager} * also possible to switch to {@link UnorderedRequestExpectationManager}
* by setting {@link #unordered()}. * by setting {@link #ignoreExpectOrder()}.
*/ */
MockRestServiceServerBuilder expectationManager(RequestExpectationManager manager); MockRestServiceServerBuilder expectationManager(RequestExpectationManager manager);
@ -231,7 +232,7 @@ public class MockRestServiceServer {
@Override @Override
public MockRestServiceServerBuilder unordered() { public MockRestServiceServerBuilder ignoreExpectOrder() {
expectationManager(new UnorderedRequestExpectationManager()); expectationManager(new UnorderedRequestExpectationManager());
return this; return this;
} }

View File

@ -23,7 +23,6 @@ import org.springframework.core.io.Resource;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.test.web.Person; import org.springframework.test.web.Person;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer; import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
@ -50,7 +49,7 @@ public class SampleTests {
@Before @Before
public void setup() { public void setup() {
this.restTemplate = new RestTemplate(); this.restTemplate = new RestTemplate();
this.mockServer = MockRestServiceServer.createServer(this.restTemplate); this.mockServer = MockRestServiceServer.bindTo(this.restTemplate).ignoreExpectOrder().build();
} }
@Test @Test

View File

@ -5045,28 +5045,63 @@ Here is an example:
---- ----
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.restTemplate(restTemplate).build(); MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(manyTimes(), requestTo("/greeting")) mockServer.expect(requestTo("/greeting")).andRespond(withSuccess());
.andRespond(withSuccess("Hello world", MediaType.TEXT_PLAIN));
// Test code that uses the above RestTemplate ... // Test code that uses the above RestTemplate ...
mockServer.verify(); mockServer.verify();
---- ----
In the above example, `MockRestServiceServer` -- the central class for client-side REST In the above example, `MockRestServiceServer`, the central class for client-side REST
tests -- configures the `RestTemplate` with a custom `ClientHttpRequestFactory` that tests, configures the `RestTemplate` with a custom `ClientHttpRequestFactory` that
asserts actual requests against expectations and returns "stub" responses. In this case asserts actual requests against expectations and returns "stub" responses. In this case
we expect a single request to "/greeting" and want to return a 200 response with we expect a request to "/greeting" and want to return a 200 response with
"text/plain" content. We could define as many additional requests and stub responses as "text/plain" content. We could define as additional expected requests and stub responses as
necessary. Once expected requests and stub responses have been defined, the `RestTemplate` can be needed. When expected requests and stub responses are defined, the `RestTemplate` can be
used in client-side code as usual. At the end of the tests `mockServer.verify()` can be used in client-side code as usual. At the end of testing `mockServer.verify()` can be
used to verify that all expected requests were performed. used to verify that all expectations have been satisfied.
The client-side test support also provides an alternative `ClientHttpRequestFactory` By default requests are expected in the order in which expectations were declared.
strategy for executing requests with a `MockMvc` instance. That allows you to You can set the `ignoreExpectOrder` option when building the server in which case
process requests using your server-side code but without running a server. all expectations are checked (in order) to find a match for a given request. That
Here is an example: means requests are allowed to come in any order. Here is an example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
server = MockRestServiceServer.bindTo(restTemplate).ignoreExpectOrder().build();
----
Even with unordered requests by default each request is allowed to execute once only.
The `expect` method provides an overloaded variant that accepts an `ExpectedCount`
argument that specifies a count range, e.g. `once`, `manyTimes`, `max`, `min`,
`between`, and so on. Here is an example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.bindTo(restTemplate).build();
mockServer.expect(times(2), requestTo("/foo")).andRespond(withSuccess());
mockServer.expect(times(3), requestTo("/bar")).andRespond(withSuccess());
// ...
mockServer.verify();
----
Note that when `ignoreExpectOrder` is not set (the default), and therefore requests
are expected in order of declaration, then that order only applies to the first of
any expected request. For example if "/foo" is expected 2 times followed by "/bar"
3 times, then there should be a request to "/foo" before there is a request to "/bar"
but aside from that subsequent "/foo" and "/bar" requests can come at any time.
As an alternative to all of the above the client-side test support also provides a
`ClientHttpRequestFactory` implementation that can be configured into a `RestTemplate`
to bind it to a `MockMvc` instance. That allows processing requests using actual
server-side logic but without running a server. Here is an example:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -5086,7 +5121,7 @@ Here is an example:
Just like with server-side tests, the fluent API for client-side tests requires a few Just like with server-side tests, the fluent API for client-side tests requires a few
static imports. Those are easy to find by searching __"MockRest*"__. Eclipse users static imports. Those are easy to find by searching __"MockRest*"__. Eclipse users
should add `"MockRestRequestMatchers.{asterisk}"` and `"MockRestResponseCreators.{asterisk}"` should add `"MockRestRequestMatchers.{asterisk}"` and `"MockRestResponseCreators.{asterisk}"`
as "favorite static members" in the Eclipse preferences under as "favorite static members" in the Eclipse preferences under
__Java -> Editor -> Content Assist -> Favorites__. __Java -> Editor -> Content Assist -> Favorites__.
That allows using content assist after typing the first character of the That allows using content assist after typing the first character of the
static method name. Other IDEs (e.g. IntelliJ) may not require any additional static method name. Other IDEs (e.g. IntelliJ) may not require any additional

View File

@ -678,5 +678,6 @@ Spring 4.3 also improves the caching abstraction as follows:
* The JUnit support in the _Spring TestContext Framework_ now requires JUnit 4.12 or higher. * The JUnit support in the _Spring TestContext Framework_ now requires JUnit 4.12 or higher.
* Server-side Spring MVC Test supports expectations on response headers with multiple values. * Server-side Spring MVC Test supports expectations on response headers with multiple values.
* Server-side Spring MVC Test parses form data request content and populates request parameters. * Server-side Spring MVC Test parses form data request content and populates request parameters.
* Client-side Spring MVC Test supports expected count of request executions (once, manyTimes, min, max, etc.) * Client-side REST test support allows indicating how many times a request is expected and
* Client-side Spring MVC Test supports expectations for form data in the request body. whether the oder of declaration for expectations should be ignored (see <<spring-mvc-test-client>>)
* Client-side REST Test supports expectations for form data in the request body.