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
相关产品推荐
相关产品推荐

