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