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.servlet.result;
018
019import java.util.Map;
020
021import javax.xml.xpath.XPathExpressionException;
022
023import org.hamcrest.Matcher;
024
025import org.springframework.lang.Nullable;
026import org.springframework.test.web.servlet.ResultMatcher;
027import org.springframework.util.AntPathMatcher;
028import org.springframework.web.util.UriComponentsBuilder;
029
030import static org.springframework.test.util.AssertionErrors.assertEquals;
031import static org.springframework.test.util.AssertionErrors.assertTrue;
032
033/**
034 * Static factory methods for {@link ResultMatcher}-based result actions.
035 *
036 * <h3>Eclipse Users</h3>
037 * <p>Consider adding this class as a Java editor favorite. To navigate to
038 * this setting, open the Preferences and type "favorites".
039 *
040 * @author Rossen Stoyanchev
041 * @author Brian Clozel
042 * @author Sam Brannen
043 * @since 3.2
044 */
045public abstract class MockMvcResultMatchers {
046
047        private static final AntPathMatcher pathMatcher = new AntPathMatcher();
048
049
050        /**
051         * Access to request-related assertions.
052         */
053        public static RequestResultMatchers request() {
054                return new RequestResultMatchers();
055        }
056
057        /**
058         * Access to assertions for the handler that handled the request.
059         */
060        public static HandlerResultMatchers handler() {
061                return new HandlerResultMatchers();
062        }
063
064        /**
065         * Access to model-related assertions.
066         */
067        public static ModelResultMatchers model() {
068                return new ModelResultMatchers();
069        }
070
071        /**
072         * Access to assertions on the selected view.
073         */
074        public static ViewResultMatchers view() {
075                return new ViewResultMatchers();
076        }
077
078        /**
079         * Access to flash attribute assertions.
080         */
081        public static FlashAttributeResultMatchers flash() {
082                return new FlashAttributeResultMatchers();
083        }
084
085        /**
086         * Asserts the request was forwarded to the given URL.
087         * <p>This method accepts only exact matches.
088         * @param expectedUrl the exact URL expected
089         */
090        public static ResultMatcher forwardedUrl(@Nullable String expectedUrl) {
091                return result -> assertEquals("Forwarded URL", expectedUrl, result.getResponse().getForwardedUrl());
092        }
093
094        /**
095         * Asserts the request was forwarded to the given URL template.
096         * <p>This method accepts exact matches against the expanded and encoded URL template.
097         * @param urlTemplate a URL template; the expanded URL will be encoded
098         * @param uriVars zero or more URI variables to populate the template
099         * @see UriComponentsBuilder#fromUriString(String)
100         */
101        public static ResultMatcher forwardedUrlTemplate(String urlTemplate, Object... uriVars) {
102                String uri = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(uriVars).encode().toUriString();
103                return forwardedUrl(uri);
104        }
105
106        /**
107         * Asserts the request was forwarded to the given URL.
108         * <p>This method accepts {@link org.springframework.util.AntPathMatcher}
109         * patterns.
110         * @param urlPattern an Ant-style path pattern to match against
111         * @since 4.0
112         * @see org.springframework.util.AntPathMatcher
113         */
114        public static ResultMatcher forwardedUrlPattern(String urlPattern) {
115                return result -> {
116                        assertTrue("'" + urlPattern + "' is not an Ant-style path pattern",
117                                        pathMatcher.isPattern(urlPattern));
118                        String url = result.getResponse().getForwardedUrl();
119                        assertTrue("Forwarded URL '" + url + "' does not match the expected URL pattern '" + urlPattern + "'",
120                                        (url != null && pathMatcher.match(urlPattern, url)));
121                };
122        }
123
124        /**
125         * Asserts the request was redirected to the given URL.
126         * <p>This method accepts only exact matches.
127         * @param expectedUrl the exact URL expected
128         */
129        public static ResultMatcher redirectedUrl(String expectedUrl) {
130                return result -> assertEquals("Redirected URL", expectedUrl, result.getResponse().getRedirectedUrl());
131        }
132
133        /**
134         * Asserts the request was redirected to the given URL template.
135         * <p>This method accepts exact matches against the expanded and encoded URL template.
136         * @param urlTemplate a URL template; the expanded URL will be encoded
137         * @param uriVars zero or more URI variables to populate the template
138         * @see UriComponentsBuilder#fromUriString(String)
139         */
140        public static ResultMatcher redirectedUrlTemplate(String urlTemplate, Object... uriVars) {
141                String uri = UriComponentsBuilder.fromUriString(urlTemplate).buildAndExpand(uriVars).encode().toUriString();
142                return redirectedUrl(uri);
143        }
144
145        /**
146         * Asserts the request was redirected to the given URL.
147         * <p>This method accepts {@link org.springframework.util.AntPathMatcher}
148         * patterns.
149         * @param urlPattern an Ant-style path pattern to match against
150         * @since 4.0
151         * @see org.springframework.util.AntPathMatcher
152         */
153        public static ResultMatcher redirectedUrlPattern(String urlPattern) {
154                return result -> {
155                        assertTrue("'" + urlPattern + "' is not an Ant-style path pattern",
156                                        pathMatcher.isPattern(urlPattern));
157                        String url = result.getResponse().getRedirectedUrl();
158                        assertTrue("Redirected URL '" + url + "' does not match the expected URL pattern '" + urlPattern + "'",
159                                        (url != null && pathMatcher.match(urlPattern, url)));
160                };
161        }
162
163        /**
164         * Access to response status assertions.
165         */
166        public static StatusResultMatchers status() {
167                return new StatusResultMatchers();
168        }
169
170        /**
171         * Access to response header assertions.
172         */
173        public static HeaderResultMatchers header() {
174                return new HeaderResultMatchers();
175        }
176
177        /**
178         * Access to response body assertions.
179         */
180        public static ContentResultMatchers content() {
181                return new ContentResultMatchers();
182        }
183
184        /**
185         * Access to response body assertions using a
186         * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression
187         * to inspect a specific subset of the body.
188         * <p>The JSON path expression can be a parameterized string using
189         * formatting specifiers as defined in
190         * {@link String#format(String, Object...)}.
191         * @param expression the JSON path expression, optionally parameterized with arguments
192         * @param args arguments to parameterize the JSON path expression with
193         * @see #jsonPath(String, Matcher)
194         * @see #jsonPath(String, Matcher, Class)
195         */
196        public static JsonPathResultMatchers jsonPath(String expression, Object... args) {
197                return new JsonPathResultMatchers(expression, args);
198        }
199
200        /**
201         * Evaluate the given <a href="https://github.com/jayway/JsonPath">JsonPath</a>
202         * expression against the response body and assert the resulting value with
203         * the given Hamcrest {@link Matcher}.
204         * @param expression the JSON path expression
205         * @param matcher a matcher for the value expected at the JSON path
206         * @see #jsonPath(String, Object...)
207         * @see #jsonPath(String, Matcher, Class)
208         */
209        public static <T> ResultMatcher jsonPath(String expression, Matcher<T> matcher) {
210                return new JsonPathResultMatchers(expression).value(matcher);
211        }
212
213        /**
214         * Evaluate the given <a href="https://github.com/jayway/JsonPath">JsonPath</a>
215         * expression against the response body and assert the resulting value with
216         * the given Hamcrest {@link Matcher}, coercing the resulting value into the
217         * given target type before applying the matcher.
218         * <p>This can be useful for matching numbers reliably &mdash; for example,
219         * to coerce an integer into a double.
220         * @param expression the JSON path expression
221         * @param matcher a matcher for the value expected at the JSON path
222         * @param targetType the target type to coerce the matching value into
223         * @since 5.2
224         * @see #jsonPath(String, Object...)
225         * @see #jsonPath(String, Matcher)
226         */
227        public static <T> ResultMatcher jsonPath(String expression, Matcher<T> matcher, Class<T> targetType) {
228                return new JsonPathResultMatchers(expression).value(matcher, targetType);
229        }
230
231        /**
232         * Access to response body assertions using an XPath expression to
233         * inspect a specific subset of the body.
234         * <p>The XPath expression can be a parameterized string using formatting
235         * specifiers as defined in {@link String#format(String, Object...)}.
236         * @param expression the XPath expression, optionally parameterized with arguments
237         * @param args arguments to parameterize the XPath expression with
238         */
239        public static XpathResultMatchers xpath(String expression, Object... args) throws XPathExpressionException {
240                return new XpathResultMatchers(expression, null, args);
241        }
242
243        /**
244         * Access to response body assertions using an XPath expression to
245         * inspect a specific subset of the body.
246         * <p>The XPath expression can be a parameterized string using formatting
247         * specifiers as defined in {@link String#format(String, Object...)}.
248         * @param expression the XPath expression, optionally parameterized with arguments
249         * @param namespaces the namespaces referenced in the XPath expression
250         * @param args arguments to parameterize the XPath expression with
251         */
252        public static XpathResultMatchers xpath(String expression, Map<String, String> namespaces, Object... args)
253                        throws XPathExpressionException {
254
255                return new XpathResultMatchers(expression, namespaces, args);
256        }
257
258        /**
259         * Access to response cookie assertions.
260         */
261        public static CookieResultMatchers cookie() {
262                return new CookieResultMatchers();
263        }
264
265}