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