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.match; 018 019import java.net.URI; 020import java.util.List; 021import java.util.Map; 022 023import javax.xml.xpath.XPathExpressionException; 024 025import org.hamcrest.Matcher; 026 027import org.springframework.http.HttpMethod; 028import org.springframework.http.client.ClientHttpRequest; 029import org.springframework.test.web.client.MockRestServiceServer; 030import org.springframework.test.web.client.RequestMatcher; 031import org.springframework.util.Assert; 032import org.springframework.util.MultiValueMap; 033import org.springframework.web.util.UriComponentsBuilder; 034 035import static org.hamcrest.MatcherAssert.assertThat; 036import static org.springframework.test.util.AssertionErrors.assertEquals; 037import static org.springframework.test.util.AssertionErrors.fail; 038 039/** 040 * Static factory methods for {@link RequestMatcher} classes. Typically used to 041 * provide input for {@link MockRestServiceServer#expect(RequestMatcher)}. 042 * 043 * <h3>Eclipse Users</h3> 044 * <p>Consider adding this class as a Java editor favorite. To navigate to 045 * this setting, open the Preferences and type "favorites". 046 * 047 * @author Craig Walls 048 * @author Rossen Stoyanchev 049 * @author Sam Brannen 050 * @since 3.2 051 */ 052public abstract class MockRestRequestMatchers { 053 054 /** 055 * Match to any request. 056 */ 057 public static RequestMatcher anything() { 058 return request -> {}; 059 } 060 061 /** 062 * Assert the {@link HttpMethod} of the request. 063 * @param method the HTTP method 064 * @return the request matcher 065 */ 066 public static RequestMatcher method(HttpMethod method) { 067 Assert.notNull(method, "'method' must not be null"); 068 return request -> assertEquals("Unexpected HttpMethod", method, request.getMethod()); 069 } 070 071 /** 072 * Assert the request URI string with the given Hamcrest matcher. 073 * @param matcher the String matcher for the expected URI 074 * @return the request matcher 075 */ 076 public static RequestMatcher requestTo(Matcher<String> matcher) { 077 Assert.notNull(matcher, "'matcher' must not be null"); 078 return request -> assertThat("Request URI", request.getURI().toString(), matcher); 079 } 080 081 /** 082 * Assert the request URI matches the given string. 083 * @param expectedUri the expected URI 084 * @return the request matcher 085 */ 086 public static RequestMatcher requestTo(String expectedUri) { 087 Assert.notNull(expectedUri, "'uri' must not be null"); 088 return request -> assertEquals("Request URI", expectedUri, request.getURI().toString()); 089 } 090 091 /** 092 * Variant of {@link #requestTo(URI)} that prepares the URI from a URI 093 * template plus optional variables via {@link UriComponentsBuilder} 094 * including encoding. 095 * @param expectedUri the expected URI template 096 * @param uriVars zero or more URI variables to populate the expected URI 097 * @return the request matcher 098 */ 099 public static RequestMatcher requestToUriTemplate(String expectedUri, Object... uriVars) { 100 Assert.notNull(expectedUri, "'uri' must not be null"); 101 URI uri = UriComponentsBuilder.fromUriString(expectedUri).buildAndExpand(uriVars).encode().toUri(); 102 return requestTo(uri); 103 } 104 105 /** 106 * Expect a request to the given URI. 107 * @param uri the expected URI 108 * @return the request matcher 109 */ 110 public static RequestMatcher requestTo(URI uri) { 111 Assert.notNull(uri, "'uri' must not be null"); 112 return request -> assertEquals("Unexpected request", uri, request.getURI()); 113 } 114 115 /** 116 * Assert request query parameter values with the given Hamcrest matcher(s). 117 */ 118 @SafeVarargs 119 public static RequestMatcher queryParam(String name, Matcher<? super String>... matchers) { 120 return request -> { 121 MultiValueMap<String, String> params = getQueryParams(request); 122 assertValueCount("query param", name, params, matchers.length); 123 for (int i = 0 ; i < matchers.length; i++) { 124 assertThat("Query param", params.get(name).get(i), matchers[i]); 125 } 126 }; 127 } 128 129 /** 130 * Assert request query parameter values. 131 */ 132 public static RequestMatcher queryParam(String name, String... expectedValues) { 133 return request -> { 134 MultiValueMap<String, String> params = getQueryParams(request); 135 assertValueCount("query param", name, params, expectedValues.length); 136 for (int i = 0 ; i < expectedValues.length; i++) { 137 assertEquals("Query param [" + name + "]", expectedValues[i], params.get(name).get(i)); 138 } 139 }; 140 } 141 142 private static MultiValueMap<String, String> getQueryParams(ClientHttpRequest request) { 143 return UriComponentsBuilder.fromUri(request.getURI()).build().getQueryParams(); 144 } 145 146 private static void assertValueCount( 147 String valueType, String name, MultiValueMap<String, String> map, int count) { 148 149 List<String> values = map.get(name); 150 String message = "Expected " + valueType + " <" + name + ">"; 151 if (values == null) { 152 fail(message + " to exist but was null"); 153 } 154 if (count > values.size()) { 155 fail(message + " to have at least <" + count + "> values but found " + values); 156 } 157 } 158 159 /** 160 * Assert request header values with the given Hamcrest matcher(s). 161 */ 162 @SafeVarargs 163 public static RequestMatcher header(String name, Matcher<? super String>... matchers) { 164 return request -> { 165 assertValueCount("header", name, request.getHeaders(), matchers.length); 166 List<String> headerValues = request.getHeaders().get(name); 167 Assert.state(headerValues != null, "No header values"); 168 for (int i = 0; i < matchers.length; i++) { 169 assertThat("Request header [" + name + "]", headerValues.get(i), matchers[i]); 170 } 171 }; 172 } 173 174 /** 175 * Assert request header values. 176 */ 177 public static RequestMatcher header(String name, String... expectedValues) { 178 return request -> { 179 assertValueCount("header", name, request.getHeaders(), expectedValues.length); 180 List<String> headerValues = request.getHeaders().get(name); 181 Assert.state(headerValues != null, "No header values"); 182 for (int i = 0; i < expectedValues.length; i++) { 183 assertEquals("Request header [" + name + "]", expectedValues[i], headerValues.get(i)); 184 } 185 }; 186 } 187 188 /** 189 * Assert that the given request header does not exist. 190 * @since 5.2 191 */ 192 public static RequestMatcher headerDoesNotExist(String name) { 193 return request -> { 194 List<String> headerValues = request.getHeaders().get(name); 195 if (headerValues != null) { 196 fail("Expected header <" + name + "> not to exist, but it exists with values: " + 197 headerValues); 198 } 199 }; 200 } 201 202 /** 203 * Access to request body matchers. 204 */ 205 public static ContentRequestMatchers content() { 206 return new ContentRequestMatchers(); 207 } 208 209 /** 210 * Access to request body matchers using a 211 * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression to 212 * inspect a specific subset of the body. The JSON path expression can be a 213 * parameterized string using formatting specifiers as defined in 214 * {@link String#format(String, Object...)}. 215 * @param expression the JSON path optionally parameterized with arguments 216 * @param args arguments to parameterize the JSON path expression with 217 */ 218 public static JsonPathRequestMatchers jsonPath(String expression, Object... args) { 219 return new JsonPathRequestMatchers(expression, args); 220 } 221 222 /** 223 * Access to request body matchers using a 224 * <a href="https://github.com/jayway/JsonPath">JsonPath</a> expression to 225 * inspect a specific subset of the body and a Hamcrest match for asserting 226 * the value found at the JSON path. 227 * @param expression the JSON path expression 228 * @param matcher a matcher for the value expected at the JSON path 229 */ 230 public static <T> RequestMatcher jsonPath(String expression, Matcher<T> matcher) { 231 return new JsonPathRequestMatchers(expression).value(matcher); 232 } 233 234 /** 235 * Access to request body matchers using an XPath to inspect a specific 236 * subset of the body. The XPath expression can be a parameterized string 237 * using formatting specifiers as defined in 238 * {@link String#format(String, Object...)}. 239 * @param expression the XPath optionally parameterized with arguments 240 * @param args arguments to parameterize the XPath expression with 241 */ 242 public static XpathRequestMatchers xpath(String expression, Object... args) throws XPathExpressionException { 243 return new XpathRequestMatchers(expression, null, args); 244 } 245 246 /** 247 * Access to response body matchers using an XPath to inspect a specific 248 * subset of the body. The XPath expression can be a parameterized string 249 * using formatting specifiers as defined in 250 * {@link String#format(String, Object...)}. 251 * @param expression the XPath optionally parameterized with arguments 252 * @param namespaces the namespaces referenced in the XPath expression 253 * @param args arguments to parameterize the XPath expression with 254 */ 255 public static XpathRequestMatchers xpath(String expression, Map<String, String> namespaces, Object... args) 256 throws XPathExpressionException { 257 258 return new XpathRequestMatchers(expression, namespaces, args); 259 } 260 261}