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 * &#47;&#47; 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}