001/* 002 * Copyright 2002-2017 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; 020import javax.servlet.http.HttpServletResponse; 021import javax.xml.transform.Source; 022import javax.xml.transform.dom.DOMSource; 023 024import org.hamcrest.Matcher; 025import org.w3c.dom.Node; 026 027import org.springframework.http.MediaType; 028import org.springframework.test.util.JsonExpectationsHelper; 029import org.springframework.test.util.XmlExpectationsHelper; 030import org.springframework.test.web.servlet.MvcResult; 031import org.springframework.test.web.servlet.ResultMatcher; 032 033import static org.hamcrest.MatcherAssert.*; 034import static org.springframework.test.util.AssertionErrors.*; 035 036/** 037 * Factory for response content assertions. 038 * 039 * <p>An instance of this class is typically accessed via 040 * {@link MockMvcResultMatchers#content}. 041 * 042 * @author Rossen Stoyanchev 043 * @since 3.2 044 */ 045public class ContentResultMatchers { 046 047 private final XmlExpectationsHelper xmlHelper; 048 049 private final JsonExpectationsHelper jsonHelper; 050 051 052 /** 053 * Protected constructor. 054 * Use {@link MockMvcResultMatchers#content()}. 055 */ 056 protected ContentResultMatchers() { 057 this.xmlHelper = new XmlExpectationsHelper(); 058 this.jsonHelper = new JsonExpectationsHelper(); 059 } 060 061 062 /** 063 * Assert the ServletResponse content type. The given content type must 064 * fully match including type, sub-type, and parameters. For checking 065 * only the type and sub-type see {@link #contentTypeCompatibleWith(String)}. 066 */ 067 public ResultMatcher contentType(String contentType) { 068 return contentType(MediaType.parseMediaType(contentType)); 069 } 070 071 /** 072 * Assert the ServletResponse content type after parsing it as a MediaType. 073 * The given content type must fully match including type, sub-type, and 074 * parameters. For checking only the type and sub-type see 075 * {@link #contentTypeCompatibleWith(MediaType)}. 076 */ 077 public ResultMatcher contentType(final MediaType contentType) { 078 return new ResultMatcher() { 079 @Override 080 public void match(MvcResult result) throws Exception { 081 String actual = result.getResponse().getContentType(); 082 assertTrue("Content type not set", actual != null); 083 assertEquals("Content type", contentType, MediaType.parseMediaType(actual)); 084 } 085 }; 086 } 087 088 /** 089 * Assert the ServletResponse content type is compatible with the given 090 * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. 091 */ 092 public ResultMatcher contentTypeCompatibleWith(String contentType) { 093 return contentTypeCompatibleWith(MediaType.parseMediaType(contentType)); 094 } 095 096 /** 097 * Assert the ServletResponse content type is compatible with the given 098 * content type as defined by {@link MediaType#isCompatibleWith(MediaType)}. 099 */ 100 public ResultMatcher contentTypeCompatibleWith(final MediaType contentType) { 101 return new ResultMatcher() { 102 @Override 103 public void match(MvcResult result) throws Exception { 104 String actual = result.getResponse().getContentType(); 105 assertTrue("Content type not set", actual != null); 106 MediaType actualContentType = MediaType.parseMediaType(actual); 107 assertTrue("Content type [" + actual + "] is not compatible with [" + contentType + "]", 108 actualContentType.isCompatibleWith(contentType)); 109 } 110 }; 111 } 112 113 /** 114 * Assert the character encoding in the ServletResponse. 115 * @see HttpServletResponse#getCharacterEncoding() 116 */ 117 public ResultMatcher encoding(final String characterEncoding) { 118 return new ResultMatcher() { 119 @Override 120 public void match(MvcResult result) { 121 String actual = result.getResponse().getCharacterEncoding(); 122 assertEquals("Character encoding", characterEncoding, actual); 123 } 124 }; 125 } 126 127 /** 128 * Assert the response body content with a Hamcrest {@link Matcher}. 129 * <pre class="code"> 130 * mockMvc.perform(get("/path")) 131 * .andExpect(content().string(containsString("text"))); 132 * </pre> 133 */ 134 public ResultMatcher string(final Matcher<? super String> matcher) { 135 return new ResultMatcher() { 136 @Override 137 public void match(MvcResult result) throws Exception { 138 assertThat("Response content", result.getResponse().getContentAsString(), matcher); 139 } 140 }; 141 } 142 143 /** 144 * Assert the response body content as a String. 145 */ 146 public ResultMatcher string(final String expectedContent) { 147 return new ResultMatcher() { 148 @Override 149 public void match(MvcResult result) throws Exception { 150 assertEquals("Response content", expectedContent, result.getResponse().getContentAsString()); 151 } 152 }; 153 } 154 155 /** 156 * Assert the response body content as a byte array. 157 */ 158 public ResultMatcher bytes(final byte[] expectedContent) { 159 return new ResultMatcher() { 160 @Override 161 public void match(MvcResult result) throws Exception { 162 assertEquals("Response content", expectedContent, result.getResponse().getContentAsByteArray()); 163 } 164 }; 165 } 166 167 /** 168 * Parse the response content and the given string as XML and assert the two 169 * are "similar" - i.e. they contain the same elements and attributes 170 * regardless of order. 171 * <p>Use of this matcher requires the <a 172 * href="http://xmlunit.sourceforge.net/">XMLUnit<a/> library. 173 * @param xmlContent the expected XML content 174 * @see MockMvcResultMatchers#xpath(String, Object...) 175 * @see MockMvcResultMatchers#xpath(String, Map, Object...) 176 */ 177 public ResultMatcher xml(final String xmlContent) { 178 return new ResultMatcher() { 179 @Override 180 public void match(MvcResult result) throws Exception { 181 String content = result.getResponse().getContentAsString(); 182 xmlHelper.assertXmlEqual(xmlContent, content); 183 } 184 }; 185 } 186 187 /** 188 * Parse the response content as {@link Node} and apply the given Hamcrest 189 * {@link Matcher}. 190 */ 191 public ResultMatcher node(final Matcher<? super Node> matcher) { 192 return new ResultMatcher() { 193 @Override 194 public void match(MvcResult result) throws Exception { 195 String content = result.getResponse().getContentAsString(); 196 xmlHelper.assertNode(content, matcher); 197 } 198 }; 199 } 200 201 /** 202 * Parse the response content as {@link DOMSource} and apply the given 203 * Hamcrest {@link Matcher}. 204 * @see <a href="https://code.google.com/p/xml-matchers/">xml-matchers</a> 205 */ 206 public ResultMatcher source(final Matcher<? super Source> matcher) { 207 return new ResultMatcher() { 208 @Override 209 public void match(MvcResult result) throws Exception { 210 String content = result.getResponse().getContentAsString(); 211 xmlHelper.assertSource(content, matcher); 212 } 213 }; 214 } 215 216 /** 217 * Parse the expected and actual strings as JSON and assert the two 218 * are "similar" - i.e. they contain the same attribute-value pairs 219 * regardless of formatting with a lenient checking (extensible, and non-strict array 220 * ordering). 221 * @param jsonContent the expected JSON content 222 * @since 4.1 223 */ 224 public ResultMatcher json(final String jsonContent) { 225 return json(jsonContent, false); 226 } 227 228 /** 229 * Parse the response content and the given string as JSON and assert the two are "similar" - 230 * i.e. they contain the same attribute-value pairs regardless of formatting. 231 * <p>Can compare in two modes, depending on {@code strict} parameter value: 232 * <ul> 233 * <li>{@code true}: strict checking. Not extensible, and strict array ordering.</li> 234 * <li>{@code false}: lenient checking. Extensible, and non-strict array ordering.</li> 235 * </ul> 236 * <p>Use of this matcher requires the <a 237 * href="https://jsonassert.skyscreamer.org/">JSONassert<a/> library. 238 * @param jsonContent the expected JSON content 239 * @param strict enables strict checking 240 * @since 4.2 241 */ 242 public ResultMatcher json(final String jsonContent, final boolean strict) { 243 return new ResultMatcher() { 244 @Override 245 public void match(MvcResult result) throws Exception { 246 String content = result.getResponse().getContentAsString(); 247 jsonHelper.assertJsonEqual(jsonContent, content, strict); 248 } 249 }; 250 } 251 252}