Spring MVC测试中集合对象不相等的原因?Mockito与JUnit是否冲突?
在Spring Boot项目的MVC测试场景中,getRecipes()方法预期返回Recipe集合,但Recipe类未重写equals()和hashCode()方法。RecipeService每次调用都会返回新的HashSet,理论上依赖同一RecipeRepository,集合内的Recipe对象应一致,两个HashSet调用equals时应返回true,但实际测试中却显示两个集合的Recipe对象不同。
测试代码
@SpringBootTest class RecipeControllerTest { @Mock private Model model; @Autowired private RecipeService recipeService; @Autowired private RecipeController recipeController; @Test public void testMVC() { try { MockMvc mockMvc = MockMvcBuilders.standaloneSetup(recipeController).build(); mockMvc.perform(MockMvcRequestBuilders.get("/")) .andExpect(status().isOk()) .andExpect(model().attribute("recipes", recipeService.getRecipes())) .andExpect(view().name("index")); } catch (Exception e) { e.printStackTrace(); } } }
RecipeService代码
public class RecipeService { private final RecipeRepository recipeRepository; public RecipeService(RecipeRepository recipeRepository) { this.recipeRepository = recipeRepository; } public Set<Recipe> getRecipes(){ log.debug("debugging message inside of "+getClass().getSimpleName()); Set<Recipe> recipes=new HashSet<>(); recipeRepository.findAll().forEach(recipes::add); return recipes; } }
测试报错信息
java.lang.AssertionError: Model attribute 'recipes' expected:<[com.arbabsoft.recipe.model.Recipe@4619854a]> but was:<[com.arbabsoft.recipe.model.Recipe@6eff05e7]> Expected :[com.arbabsoft.recipe.model.Recipe@4619854a] Actual :[com.arbabsoft.recipe.model.Recipe@6eff05e7]
但将测试类修改为以下配置后,测试通过:
@Mock private RecipeService recipeService; @InjectMocks private RecipeController recipeController;
请问导致该现象的原因是什么?是否存在JUnit、Mockito与Spring MVC Test的冲突?
1. 未重写equals/hashCode的核心影响
HashSet的equals()比较逻辑是:两个集合大小相同,且每个元素都能在对方集合中找到相等的元素。但Recipe类未重写equals()和hashCode(),默认使用Object类的实现——直接比较对象的内存地址,而非业务逻辑上的相等。
2. 两次getRecipes()调用返回的对象实例不同
原测试代码中,你调用了两次getRecipes():一次是测试代码里的recipeService.getRecipes()作为预期值,另一次是Controller内部调用recipeService.getRecipes()生成实际值。即使RecipeRepository返回的数据逻辑一致,如果Repository每次查询都会创建新的Recipe对象实例(比如JPA的EntityManager每次查询都会生成新的实体对象),那么两次得到的Recipe对象内存地址完全不同,最终导致HashSet的equals比较失败。
3. 修改后测试通过的本质
当你用@Mock模拟RecipeService、@InjectMocks注入Controller时,recipeService.getRecipes()的返回值是你可以控制的Mock结果——两次调用返回的是同一个Set实例(或你指定的相同实例),内存地址一致,所以equals比较自然通过。这和Spring、Mockito的冲突无关,是两种测试策略的差异导致的:原写法用真实的Service和Repository,两次查询生成不同对象实例;修改后的写法用Mock Service,返回固定实例。
- 核心原因是Recipe类未重写equals/hashCode,且两次查询返回了不同内存地址的Recipe实例,导致HashSet的equals比较不通过。
- 不存在JUnit、Mockito与Spring MVC Test的冲突,两种测试写法的本质区别是是否使用真实的业务组件。
内容的提问来源于stack exchange,提问作者fast

