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 org.hamcrest.Matcher; 020 021import org.springframework.test.web.servlet.MvcResult; 022import org.springframework.test.web.servlet.ResultMatcher; 023import org.springframework.ui.ModelMap; 024import org.springframework.validation.BindingResult; 025import org.springframework.validation.Errors; 026import org.springframework.web.servlet.ModelAndView; 027 028import static org.hamcrest.MatcherAssert.*; 029import static org.springframework.test.util.AssertionErrors.*; 030 031/** 032 * Factory for assertions on the model. 033 * 034 * <p>An instance of this class is typically accessed via 035 * {@link MockMvcResultMatchers#model}. 036 * 037 * @author Rossen Stoyanchev 038 * @since 3.2 039 */ 040public class ModelResultMatchers { 041 042 /** 043 * Protected constructor. 044 * Use {@link MockMvcResultMatchers#model()}. 045 */ 046 protected ModelResultMatchers() { 047 } 048 049 050 /** 051 * Assert a model attribute value with the given Hamcrest {@link Matcher}. 052 */ 053 public <T> ResultMatcher attribute(final String name, final Matcher<T> matcher) { 054 return new ResultMatcher() { 055 @Override 056 @SuppressWarnings("unchecked") 057 public void match(MvcResult result) throws Exception { 058 ModelAndView mav = getModelAndView(result); 059 assertThat("Model attribute '" + name + "'", (T) mav.getModel().get(name), matcher); 060 } 061 }; 062 } 063 064 /** 065 * Assert a model attribute value. 066 */ 067 public ResultMatcher attribute(final String name, final Object value) { 068 return new ResultMatcher() { 069 @Override 070 public void match(MvcResult result) throws Exception { 071 ModelAndView mav = getModelAndView(result); 072 assertEquals("Model attribute '" + name + "'", value, mav.getModel().get(name)); 073 } 074 }; 075 } 076 077 /** 078 * Assert the given model attributes exist. 079 */ 080 public ResultMatcher attributeExists(final String... names) { 081 return new ResultMatcher() { 082 @Override 083 public void match(MvcResult result) throws Exception { 084 ModelAndView mav = getModelAndView(result); 085 for (String name : names) { 086 assertTrue("Model attribute '" + name + "' does not exist", mav.getModel().get(name) != null); 087 } 088 } 089 }; 090 } 091 092 /** 093 * Assert the given model attributes do not exist 094 */ 095 public ResultMatcher attributeDoesNotExist(final String... names) { 096 return new ResultMatcher() { 097 @Override 098 public void match(MvcResult result) throws Exception { 099 ModelAndView mav = getModelAndView(result); 100 for (String name : names) { 101 assertTrue("Model attribute '" + name + "' exists", mav.getModel().get(name) == null); 102 } 103 } 104 }; 105 } 106 107 /** 108 * Assert the given model attribute(s) have errors. 109 */ 110 public ResultMatcher attributeErrorCount(final String name, final int expectedCount) { 111 return new ResultMatcher() { 112 public void match(MvcResult result) throws Exception { 113 ModelAndView mav = getModelAndView(result); 114 Errors errors = getBindingResult(mav, name); 115 assertEquals("Binding/validation error count for attribute '" + name + "', ", 116 expectedCount, errors.getErrorCount()); 117 } 118 }; 119 } 120 121 /** 122 * Assert the given model attribute(s) have errors. 123 */ 124 public ResultMatcher attributeHasErrors(final String... names) { 125 return new ResultMatcher() { 126 public void match(MvcResult mvcResult) throws Exception { 127 ModelAndView mav = getModelAndView(mvcResult); 128 for (String name : names) { 129 BindingResult result = getBindingResult(mav, name); 130 assertTrue("No errors for attribute '" + name + "'", result.hasErrors()); 131 } 132 } 133 }; 134 } 135 136 /** 137 * Assert the given model attribute(s) do not have errors. 138 */ 139 public ResultMatcher attributeHasNoErrors(final String... names) { 140 return new ResultMatcher() { 141 public void match(MvcResult mvcResult) throws Exception { 142 ModelAndView mav = getModelAndView(mvcResult); 143 for (String name : names) { 144 BindingResult result = getBindingResult(mav, name); 145 assertTrue("Unexpected errors for attribute '" + name + "': " + result.getAllErrors(), 146 !result.hasErrors()); 147 } 148 } 149 }; 150 } 151 152 /** 153 * Assert the given model attribute field(s) have errors. 154 */ 155 public ResultMatcher attributeHasFieldErrors(final String name, final String... fieldNames) { 156 return new ResultMatcher() { 157 public void match(MvcResult mvcResult) throws Exception { 158 ModelAndView mav = getModelAndView(mvcResult); 159 BindingResult result = getBindingResult(mav, name); 160 assertTrue("No errors for attribute '" + name + "'", result.hasErrors()); 161 for (final String fieldName : fieldNames) { 162 boolean hasFieldErrors = result.hasFieldErrors(fieldName); 163 assertTrue("No errors for field '" + fieldName + "' of attribute '" + name + "'", hasFieldErrors); 164 } 165 } 166 }; 167 } 168 169 /** 170 * Assert a field error code for a model attribute using exact String match. 171 * @since 4.1 172 */ 173 public ResultMatcher attributeHasFieldErrorCode(final String name, final String fieldName, final String error) { 174 return new ResultMatcher() { 175 public void match(MvcResult mvcResult) throws Exception { 176 ModelAndView mav = getModelAndView(mvcResult); 177 BindingResult result = getBindingResult(mav, name); 178 assertTrue("No errors for attribute '" + name + "'", result.hasErrors()); 179 boolean hasFieldErrors = result.hasFieldErrors(fieldName); 180 assertTrue("No errors for field '" + fieldName + "' of attribute '" + name + "'", hasFieldErrors); 181 String code = result.getFieldError(fieldName).getCode(); 182 assertTrue("Expected error code '" + error + "' but got '" + code + "'", code.equals(error)); 183 } 184 }; 185 } 186 187 /** 188 * Assert a field error code for a model attribute using a {@link org.hamcrest.Matcher}. 189 * @since 4.1 190 */ 191 public <T> ResultMatcher attributeHasFieldErrorCode(final String name, final String fieldName, 192 final Matcher<? super String> matcher) { 193 194 return new ResultMatcher() { 195 @Override 196 public void match(MvcResult mvcResult) throws Exception { 197 ModelAndView mav = getModelAndView(mvcResult); 198 BindingResult result = getBindingResult(mav, name); 199 assertTrue("No errors for attribute: [" + name + "]", result.hasErrors()); 200 boolean hasFieldErrors = result.hasFieldErrors(fieldName); 201 assertTrue("No errors for field '" + fieldName + "' of attribute '" + name + "'", hasFieldErrors); 202 String code = result.getFieldError(fieldName).getCode(); 203 assertThat("Field name '" + fieldName + "' of attribute '" + name + "'", code, matcher); 204 } 205 }; 206 } 207 208 /** 209 * Assert the total number of errors in the model. 210 */ 211 public <T> ResultMatcher errorCount(final int expectedCount) { 212 return new ResultMatcher() { 213 @Override 214 public void match(MvcResult result) throws Exception { 215 int actualCount = getErrorCount(getModelAndView(result).getModelMap()); 216 assertEquals("Binding/validation error count", expectedCount, actualCount); 217 } 218 }; 219 } 220 221 /** 222 * Assert the model has errors. 223 */ 224 public <T> ResultMatcher hasErrors() { 225 return new ResultMatcher() { 226 @Override 227 public void match(MvcResult result) throws Exception { 228 int count = getErrorCount(getModelAndView(result).getModelMap()); 229 assertTrue("Expected binding/validation errors", count != 0); 230 } 231 }; 232 } 233 234 /** 235 * Assert the model has no errors. 236 */ 237 public <T> ResultMatcher hasNoErrors() { 238 return new ResultMatcher() { 239 @Override 240 public void match(MvcResult result) throws Exception { 241 ModelAndView mav = getModelAndView(result); 242 for (Object value : mav.getModel().values()) { 243 if (value instanceof Errors) { 244 assertTrue("Unexpected binding/validation errors: " + value, !((Errors) value).hasErrors()); 245 } 246 } 247 } 248 }; 249 } 250 251 /** 252 * Assert the number of model attributes. 253 */ 254 public <T> ResultMatcher size(final int size) { 255 return new ResultMatcher() { 256 @Override 257 public void match(MvcResult result) throws Exception { 258 ModelAndView mav = getModelAndView(result); 259 int actual = 0; 260 for (String key : mav.getModel().keySet()) { 261 if (!key.startsWith(BindingResult.MODEL_KEY_PREFIX)) { 262 actual++; 263 } 264 } 265 assertEquals("Model size", size, actual); 266 } 267 }; 268 } 269 270 private ModelAndView getModelAndView(MvcResult mvcResult) { 271 ModelAndView mav = mvcResult.getModelAndView(); 272 assertTrue("No ModelAndView found", mav != null); 273 return mav; 274 } 275 276 private BindingResult getBindingResult(ModelAndView mav, String name) { 277 BindingResult result = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + name); 278 assertTrue("No BindingResult for attribute: " + name, result != null); 279 return result; 280 } 281 282 private int getErrorCount(ModelMap model) { 283 int count = 0; 284 for (Object value : model.values()) { 285 if (value instanceof Errors) { 286 count += ((Errors) value).getErrorCount(); 287 } 288 } 289 return count; 290 } 291 292}