You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 23:36:17