001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.web.client; 018 019import java.io.IOException; 020import java.net.URI; 021 022import org.springframework.http.HttpMethod; 023import org.springframework.http.client.AsyncClientHttpRequest; 024import org.springframework.http.client.AsyncClientHttpRequestFactory; 025import org.springframework.http.client.ClientHttpRequest; 026import org.springframework.http.client.ClientHttpRequestFactory; 027import org.springframework.http.client.ClientHttpResponse; 028import org.springframework.mock.http.client.MockAsyncClientHttpRequest; 029import org.springframework.util.Assert; 030import org.springframework.web.client.AsyncRestTemplate; 031import org.springframework.web.client.RestTemplate; 032import org.springframework.web.client.support.RestGatewaySupport; 033 034/** 035 * <strong>Main entry point for client-side REST testing</strong>. Used for tests 036 * that involve direct or indirect use of the {@link RestTemplate}. Provides a 037 * way to set up expected requests that will be performed through the 038 * {@code RestTemplate} as well as mock responses to send back thus removing the 039 * need for an actual server. 040 * 041 * <p>Below is an example that assumes static imports from 042 * {@code MockRestRequestMatchers}, {@code MockRestResponseCreators}, 043 * and {@code ExpectedCount}: 044 * 045 * <pre class="code"> 046 * RestTemplate restTemplate = new RestTemplate() 047 * MockRestServiceServer server = MockRestServiceServer.bindTo(restTemplate).build(); 048 * 049 * server.expect(manyTimes(), requestTo("/hotels/42")).andExpect(method(HttpMethod.GET)) 050 * .andRespond(withSuccess("{ \"id\" : \"42\", \"name\" : \"Holiday Inn\"}", MediaType.APPLICATION_JSON)); 051 * 052 * Hotel hotel = restTemplate.getForObject("/hotels/{id}", Hotel.class, 42); 053 * // Use the hotel instance... 054 * 055 * // Verify all expectations met 056 * server.verify(); 057 * </pre> 058 * 059 * <p>Note that as an alternative to the above you can also set the 060 * {@link MockMvcClientHttpRequestFactory} on a {@code RestTemplate} which 061 * allows executing requests against an instance of 062 * {@link org.springframework.test.web.servlet.MockMvc MockMvc}. 063 * 064 * @author Craig Walls 065 * @author Rossen Stoyanchev 066 * @since 3.2 067 */ 068public class MockRestServiceServer { 069 070 private final RequestExpectationManager expectationManager; 071 072 073 /** 074 * Private constructor with {@code RequestExpectationManager}. 075 * See static builder methods and {@code createServer} shortcut methods. 076 */ 077 private MockRestServiceServer(RequestExpectationManager expectationManager) { 078 this.expectationManager = expectationManager; 079 } 080 081 082 /** 083 * Set up an expectation for a single HTTP request. The returned 084 * {@link ResponseActions} can be used to set up further expectations as 085 * well as to define the response. 086 * <p>This method may be invoked any number times before starting to make 087 * request through the underlying {@code RestTemplate} in order to set up 088 * all expected requests. 089 * @param matcher request matcher 090 * @return a representation of the expectation 091 */ 092 public ResponseActions expect(RequestMatcher matcher) { 093 return expect(ExpectedCount.once(), matcher); 094 } 095 096 /** 097 * An alternative to {@link #expect(RequestMatcher)} that also indicates how 098 * many times the request is expected to be executed. 099 * <p>When request expectations have an expected count greater than one, only 100 * the first execution is expected to match the order of declaration. Subsequent 101 * request executions may be inserted anywhere thereafter. 102 * @param count the expected count 103 * @param matcher request matcher 104 * @return a representation of the expectation 105 * @since 4.3 106 */ 107 public ResponseActions expect(ExpectedCount count, RequestMatcher matcher) { 108 return this.expectationManager.expectRequest(count, matcher); 109 } 110 111 /** 112 * Verify that all expected requests set up via 113 * {@link #expect(RequestMatcher)} were indeed performed. 114 * @throws AssertionError when some expectations were not met 115 */ 116 public void verify() { 117 this.expectationManager.verify(); 118 } 119 120 /** 121 * Reset the internal state removing all expectations and recorded requests. 122 */ 123 public void reset() { 124 this.expectationManager.reset(); 125 } 126 127 128 /** 129 * Return a builder for a {@code MockRestServiceServer} that should be used 130 * to reply to the given {@code RestTemplate}. 131 * @since 4.3 132 */ 133 public static MockRestServiceServerBuilder bindTo(RestTemplate restTemplate) { 134 return new DefaultBuilder(restTemplate); 135 } 136 137 /** 138 * Return a builder for a {@code MockRestServiceServer} that should be used 139 * to reply to the given {@code AsyncRestTemplate}. 140 * @since 4.3 141 */ 142 public static MockRestServiceServerBuilder bindTo(AsyncRestTemplate asyncRestTemplate) { 143 return new DefaultBuilder(asyncRestTemplate); 144 } 145 146 /** 147 * Return a builder for a {@code MockRestServiceServer} that should be used 148 * to reply to the given {@code RestGatewaySupport}. 149 * @since 4.3 150 */ 151 public static MockRestServiceServerBuilder bindTo(RestGatewaySupport restGateway) { 152 Assert.notNull(restGateway, "'gatewaySupport' must not be null"); 153 return new DefaultBuilder(restGateway.getRestTemplate()); 154 } 155 156 157 /** 158 * A shortcut for {@code bindTo(restTemplate).build()}. 159 * @param restTemplate the RestTemplate to set up for mock testing 160 * @return the mock server 161 */ 162 public static MockRestServiceServer createServer(RestTemplate restTemplate) { 163 return bindTo(restTemplate).build(); 164 } 165 166 /** 167 * A shortcut for {@code bindTo(asyncRestTemplate).build()}. 168 * @param asyncRestTemplate the AsyncRestTemplate to set up for mock testing 169 * @return the created mock server 170 */ 171 public static MockRestServiceServer createServer(AsyncRestTemplate asyncRestTemplate) { 172 return bindTo(asyncRestTemplate).build(); 173 } 174 175 /** 176 * A shortcut for {@code bindTo(restGateway).build()}. 177 * @param restGateway the REST gateway to set up for mock testing 178 * @return the created mock server 179 */ 180 public static MockRestServiceServer createServer(RestGatewaySupport restGateway) { 181 return bindTo(restGateway).build(); 182 } 183 184 185 /** 186 * Builder to create a {@code MockRestServiceServer}. 187 */ 188 public interface MockRestServiceServerBuilder { 189 190 /** 191 * Whether to allow expected requests to be executed in any order not 192 * necessarily matching the order of declaration. 193 * <p>When set to "true" this is effectively a shortcut for:<br> 194 * {@code builder.build(new UnorderedRequestExpectationManager)}. 195 * @param ignoreExpectOrder whether to ignore the order of expectations 196 */ 197 MockRestServiceServerBuilder ignoreExpectOrder(boolean ignoreExpectOrder); 198 199 /** 200 * Build the {@code MockRestServiceServer} and set up the underlying 201 * {@code RestTemplate} or {@code AsyncRestTemplate} with a 202 * {@link ClientHttpRequestFactory} that creates mock requests. 203 */ 204 MockRestServiceServer build(); 205 206 /** 207 * An overloaded build alternative that accepts a custom 208 * {@link RequestExpectationManager}. 209 */ 210 MockRestServiceServer build(RequestExpectationManager manager); 211 } 212 213 214 private static class DefaultBuilder implements MockRestServiceServerBuilder { 215 216 private final RestTemplate restTemplate; 217 218 private final AsyncRestTemplate asyncRestTemplate; 219 220 private boolean ignoreExpectOrder; 221 222 public DefaultBuilder(RestTemplate restTemplate) { 223 Assert.notNull(restTemplate, "RestTemplate must not be null"); 224 this.restTemplate = restTemplate; 225 this.asyncRestTemplate = null; 226 } 227 228 public DefaultBuilder(AsyncRestTemplate asyncRestTemplate) { 229 Assert.notNull(asyncRestTemplate, "AsyncRestTemplate must not be null"); 230 this.restTemplate = null; 231 this.asyncRestTemplate = asyncRestTemplate; 232 } 233 234 @Override 235 public MockRestServiceServerBuilder ignoreExpectOrder(boolean ignoreExpectOrder) { 236 this.ignoreExpectOrder = ignoreExpectOrder; 237 return this; 238 } 239 240 @Override 241 public MockRestServiceServer build() { 242 if (this.ignoreExpectOrder) { 243 return build(new UnorderedRequestExpectationManager()); 244 } 245 else { 246 return build(new SimpleRequestExpectationManager()); 247 } 248 } 249 250 @Override 251 public MockRestServiceServer build(RequestExpectationManager manager) { 252 MockRestServiceServer server = new MockRestServiceServer(manager); 253 MockClientHttpRequestFactory factory = server.new MockClientHttpRequestFactory(); 254 if (this.restTemplate != null) { 255 this.restTemplate.setRequestFactory(factory); 256 } 257 if (this.asyncRestTemplate != null) { 258 this.asyncRestTemplate.setAsyncRequestFactory(factory); 259 } 260 return server; 261 } 262 } 263 264 265 /** 266 * Mock ClientHttpRequestFactory that creates requests by iterating 267 * over the list of expected {@link DefaultRequestExpectation}'s. 268 */ 269 private class MockClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory { 270 271 @Override 272 public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) { 273 return createRequestInternal(uri, httpMethod); 274 } 275 276 @Override 277 public AsyncClientHttpRequest createAsyncRequest(URI uri, HttpMethod httpMethod) { 278 return createRequestInternal(uri, httpMethod); 279 } 280 281 private MockAsyncClientHttpRequest createRequestInternal(URI uri, HttpMethod method) { 282 Assert.notNull(uri, "'uri' must not be null"); 283 Assert.notNull(method, "'httpMethod' must not be null"); 284 285 return new MockAsyncClientHttpRequest(method, uri) { 286 287 @Override 288 protected ClientHttpResponse executeInternal() throws IOException { 289 ClientHttpResponse response = expectationManager.validateRequest(this); 290 setResponse(response); 291 return response; 292 } 293 }; 294 } 295 } 296 297}