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; 018 019import java.util.Collections; 020import java.util.Comparator; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025 026import org.springframework.util.ObjectUtils; 027import org.springframework.web.servlet.ModelAndView; 028 029import static org.springframework.test.util.AssertionErrors.*; 030 031/** 032 * A collection of assertions intended to simplify testing scenarios dealing 033 * with Spring Web MVC {@link org.springframework.web.servlet.ModelAndView 034 * ModelAndView} objects. 035 * 036 * <p>Intended for use with JUnit 4 and TestNG. All {@code assert*()} methods 037 * throw {@link AssertionError}s. 038 * 039 * @author Sam Brannen 040 * @author Alef Arendsen 041 * @author Bram Smeets 042 * @since 2.5 043 * @see org.springframework.web.servlet.ModelAndView 044 */ 045public abstract class ModelAndViewAssert { 046 047 /** 048 * Checks whether the model value under the given {@code modelName} 049 * exists and checks it type, based on the {@code expectedType}. If the 050 * model entry exists and the type matches, the model value is returned. 051 * @param mav ModelAndView to test against (never {@code null}) 052 * @param modelName name of the object to add to the model (never {@code null}) 053 * @param expectedType expected type of the model value 054 * @return the model value 055 */ 056 @SuppressWarnings("unchecked") 057 public static <T> T assertAndReturnModelAttributeOfType(ModelAndView mav, String modelName, Class<T> expectedType) { 058 assertTrue("ModelAndView is null", mav != null); 059 Object obj = mav.getModel().get(modelName); 060 assertTrue("Model attribute with name '" + modelName + "' is null", obj != null); 061 assertTrue("Model attribute is not of expected type '" + expectedType.getName() + "' but rather of type '" + 062 obj.getClass().getName() + "'", expectedType.isAssignableFrom(obj.getClass())); 063 return (T) obj; 064 } 065 066 /** 067 * Compare each individual entry in a list, without first sorting the lists. 068 * @param mav ModelAndView to test against (never {@code null}) 069 * @param modelName name of the object to add to the model (never {@code null}) 070 * @param expectedList the expected list 071 */ 072 @SuppressWarnings("rawtypes") 073 public static void assertCompareListModelAttribute(ModelAndView mav, String modelName, List expectedList) { 074 assertTrue("ModelAndView is null", mav != null); 075 List modelList = assertAndReturnModelAttributeOfType(mav, modelName, List.class); 076 assertTrue("Size of model list is '" + modelList.size() + "' while size of expected list is '" + 077 expectedList.size() + "'", expectedList.size() == modelList.size()); 078 assertTrue("List in model under name '" + modelName + "' is not equal to the expected list.", 079 expectedList.equals(modelList)); 080 } 081 082 /** 083 * Assert whether or not a model attribute is available. 084 * @param mav ModelAndView to test against (never {@code null}) 085 * @param modelName name of the object to add to the model (never {@code null}) 086 */ 087 public static void assertModelAttributeAvailable(ModelAndView mav, String modelName) { 088 assertTrue("ModelAndView is null", mav != null); 089 assertTrue("Model attribute with name '" + modelName + "' is not available", 090 mav.getModel().containsKey(modelName)); 091 } 092 093 /** 094 * Compare a given {@code expectedValue} to the value from the model 095 * bound under the given {@code modelName}. 096 * @param mav ModelAndView to test against (never {@code null}) 097 * @param modelName name of the object to add to the model (never {@code null}) 098 * @param expectedValue the model value 099 */ 100 public static void assertModelAttributeValue(ModelAndView mav, String modelName, Object expectedValue) { 101 assertTrue("ModelAndView is null", mav != null); 102 Object modelValue = assertAndReturnModelAttributeOfType(mav, modelName, Object.class); 103 assertTrue("Model value with name '" + modelName + "' is not the same as the expected value which was '" + 104 expectedValue + "'", modelValue.equals(expectedValue)); 105 } 106 107 /** 108 * Inspect the {@code expectedModel} to see if all elements in the 109 * model appear and are equal. 110 * @param mav ModelAndView to test against (never {@code null}) 111 * @param expectedModel the expected model 112 */ 113 public static void assertModelAttributeValues(ModelAndView mav, Map<String, Object> expectedModel) { 114 assertTrue("ModelAndView is null", mav != null); 115 Map<String, Object> model = mav.getModel(); 116 117 if (!model.keySet().equals(expectedModel.keySet())) { 118 StringBuilder sb = new StringBuilder("Keyset of expected model does not match.\n"); 119 appendNonMatchingSetsErrorMessage(expectedModel.keySet(), model.keySet(), sb); 120 fail(sb.toString()); 121 } 122 123 StringBuilder sb = new StringBuilder(); 124 for (String modelName : model.keySet()) { 125 Object assertionValue = expectedModel.get(modelName); 126 Object mavValue = model.get(modelName); 127 if (!assertionValue.equals(mavValue)) { 128 sb.append("Value under name '").append(modelName).append("' differs, should have been '").append( 129 assertionValue).append("' but was '").append(mavValue).append("'\n"); 130 } 131 } 132 133 if (sb.length() != 0) { 134 sb.insert(0, "Values of expected model do not match.\n"); 135 fail(sb.toString()); 136 } 137 } 138 139 /** 140 * Compare each individual entry in a list after having sorted both lists 141 * (optionally using a comparator). 142 * @param mav ModelAndView to test against (never {@code null}) 143 * @param modelName name of the object to add to the model (never {@code null}) 144 * @param expectedList the expected list 145 * @param comparator the comparator to use (may be {@code null}). If not 146 * specifying the comparator, both lists will be sorted not using any comparator. 147 */ 148 @SuppressWarnings({"unchecked", "rawtypes"}) 149 public static void assertSortAndCompareListModelAttribute( 150 ModelAndView mav, String modelName, List expectedList, Comparator comparator) { 151 152 assertTrue("ModelAndView is null", mav != null); 153 List modelList = assertAndReturnModelAttributeOfType(mav, modelName, List.class); 154 assertTrue("Size of model list is '" + modelList.size() + "' while size of expected list is '" + 155 expectedList.size() + "'", expectedList.size() == modelList.size()); 156 157 if (comparator != null) { 158 Collections.sort(modelList, comparator); 159 Collections.sort(expectedList, comparator); 160 } 161 else { 162 Collections.sort(modelList); 163 Collections.sort(expectedList); 164 } 165 166 assertTrue("List in model under name '" + modelName + "' is not equal to the expected list.", 167 expectedList.equals(modelList)); 168 } 169 170 /** 171 * Check to see if the view name in the ModelAndView matches the given 172 * {@code expectedName}. 173 * @param mav ModelAndView to test against (never {@code null}) 174 * @param expectedName the name of the model value 175 */ 176 public static void assertViewName(ModelAndView mav, String expectedName) { 177 assertTrue("ModelAndView is null", mav != null); 178 assertTrue("View name is not equal to '" + expectedName + "' but was '" + mav.getViewName() + "'", 179 ObjectUtils.nullSafeEquals(expectedName, mav.getViewName())); 180 } 181 182 183 private static void appendNonMatchingSetsErrorMessage( 184 Set<String> assertionSet, Set<String> incorrectSet, StringBuilder sb) { 185 186 Set<String> tempSet = new HashSet<String>(incorrectSet); 187 tempSet.removeAll(assertionSet); 188 189 if (!tempSet.isEmpty()) { 190 sb.append("Set has too many elements:\n"); 191 for (Object element : tempSet) { 192 sb.append('-'); 193 sb.append(element); 194 sb.append('\n'); 195 } 196 } 197 198 tempSet = new HashSet<String>(assertionSet); 199 tempSet.removeAll(incorrectSet); 200 201 if (!tempSet.isEmpty()) { 202 sb.append("Set is missing elements:\n"); 203 for (Object element : tempSet) { 204 sb.append('-'); 205 sb.append(element); 206 sb.append('\n'); 207 } 208 } 209 } 210 211}