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