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

Spring Boot中JUnit与Mockito测试时InMemory仓库save方法参数接收及调试异常问题

问题分析与解决方案

我一眼就看出问题所在了——你用错了Mockito的Mock对象!核心原因是对@Mock注解的理解偏差,导致调试时根本碰不到真实的InMemory逻辑。

问题根源

被@Mock修饰的inMemory是一个完全模拟的“傀儡对象”,Mockito会为它生成动态代理类,所有方法默认都是空实现(返回null或默认值),不会执行你写的save方法里的真实代码。所以调试时看不到this对象、访问不了users Map,都是因为你操作的根本不是自己写的InMemory实例。

解决步骤

1. 用@Spy替换@Mock,保留真实逻辑

如果你需要验证方法调用次数,同时还要执行InMemory的真实save逻辑,应该用@Spy注解来包装真实的InMemory实例。Spy对象会保留原类的真实业务逻辑,同时允许你验证方法交互。

修改后的测试类代码:

@RunWith(MockitoJUnitRunner.class)
public class TestUserService {
    @InjectMocks
    UserService userService;
    // 替换@Mock为@Spy,并传入真实实例
    @Spy
    InMemory inMemory = new InMemory();

    // 注意:@RunWith(MockitoJUnitRunner.class)已经自动初始化Mock/Spy,下面的init方法可以删掉
    // @Before
    // public void init(){
    //     MockitoAnnotations.initMocks(this);
    // }

    @Test
    public void registry(){
        System.out.println("hi");
        UserDto userDto=new UserDto(1L,"abed","alrhman",26);
        UserDto userDto1=new UserDto(1L,"abdallah","almasre",27);
        UserDto userDto2=new UserDto(1L,"bisher","alahmad",35);
        UserDto userDto3=new UserDto(1L,"ibrahem","alrabe",32);
        
        userService.registry(userDto);
        userService.registry(userDto1);
        userService.registry(userDto2);
        userService.registry(userDto3);
        
        System.out.println("\"registry\" = " + "registry");
        
        // 注意:如果User类没重写equals方法,直接传userDto.ToDTO(userDto)可能匹配失败
        // 建议用any(User.class)匹配任意User对象,或者重写User的equals/hashCode
        verify(inMemory,times(4)).save(any(User.class));
        // 若要精确匹配某一个User,确保User类重写equals方法
        // verify(inMemory,times(1)).save(userDto1.ToDTO(userDto1));
    }
}

2. 关键概念区分

  • @Mock:生成完全模拟的对象,所有方法默认空实现,适合只需要模拟返回值、不需要真实逻辑的场景。
  • @Spy:基于真实实例创建代理,默认执行真实方法逻辑,适合需要验证交互同时保留真实业务的场景。

3. 额外优化建议

  • 删掉冗余的MockitoAnnotations.initMocks(this):@RunWith(MockitoJUnitRunner.class)已经会自动初始化所有@Mock/@Spy/@InjectMocks对象,重复初始化可能引发奇怪问题。
  • 修正方法命名:Java规范中方法名应该用驼峰式,建议把ToDTO改成toDto,更符合编码习惯。
  • 参数匹配问题:如果User类没重写equals和hashCode,直接用具体对象作为verify参数会匹配失败,此时可以用any(User.class)来匹配任意User类型参数。

总结

你的场景需要执行InMemory的真实保存逻辑,同时验证方法调用次数,所以@Spy才是正确选择。换成@Spy后,调试就能正常进入save方法,看到this对象和users Map了。

内容的提问来源于stack exchange,提问作者Abdalrhman Alkraien

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 15:14:08