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