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