Mockito出现UnnecessaryStubbingException异常的原因及解决方法
Mockito抛出UnnecessaryStubbingException的原因及解决办法
问题描述
我编写了RoleServiceImpl业务类及其测试类RoleServiceImplTest,在测试类的@BeforeEach方法中对RoleDao的两个方法设置了Stub。运行测试时抛出UnnecessaryStubbingException,提示when(roleDao.findByAuthority(USER))这一Stub不必要,但该Stub是getRoleByName测试方法所需。
业务类代码:
package pp.spring_bootstrap.service; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import pp.spring_bootstrap.dao.RoleDao; import pp.spring_bootstrap.models.Role; import java.util.HashSet; import java.util.Set; @Service @Transactional(readOnly = true) public class RoleServiceImpl implements RoleService { private final RoleDao roleDao; public RoleServiceImpl(RoleDao roleDao) { this.roleDao = roleDao; } @Override public Role getRoleByName(String name) { return roleDao.findByAuthority(name); } @Override public Set<Role> getAdminRoleSet() { return new HashSet<>(roleDao.findByAuthorityOrAuthority("USER", "ADMIN")); } }
测试类代码:
@ExtendWith(MockitoExtension.class) class RoleServiceImplTest { @InjectMocks RoleServiceImpl roleService; @Mock RoleDao roleDao; static final String USER = "USER"; static final String ADMIN = "ADMIN"; @BeforeEach void setUp() { when(roleDao.findByAuthority(USER)).thenReturn(new Role(USER)); when(roleDao.findByAuthorityOrAuthority(USER, ADMIN)) .thenReturn(List.of(new Role(USER), new Role(ADMIN))); } @Test void getRoleByName() { assertThat(roleService.getRoleByName(USER)).extracting(Role::getAuthority).isEqualTo(USER); verify(roleDao, times(1)).findByAuthority(USER); } @Test void getAdminRoleSet() { var adminRoles = roleService.getAdminRoleSet().stream() .map(Role::getAuthority) .toList(); assertThat(adminRoles.size()).isEqualTo(2); assertThat(adminRoles).asList().contains(ADMIN, USER); verify(roleDao, times(1)).findByAuthorityOrAuthority(USER, ADMIN); } }
异常信息:
org.mockito.exceptions.misusing.UnnecessaryStubbingException: Unnecessary stubbings detected. Clean & maintainable test code requires zero unnecessary code. Following stubbings are unnecessary (click to navigate to relevant line of code): 1. -> at pp.spring_bootstrap.service.RoleServiceImplTest.setUp(RoleServiceImplTest.java:29) Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.
原因分析
Mockito默认启用STRICT_STUBS严格模式,该模式会检查所有创建的Stub是否在当前测试方法中被调用。
由于每个测试方法独立运行:
- 执行
getAdminRoleSet测试时,@BeforeEach会创建两个Stub,但这个测试只调用了roleDao.findByAuthorityOrAuthority,没有用到roleDao.findByAuthority(USER)的Stub,Mockito判定该Stub多余,抛出异常。 - 同理,执行
getRoleByName测试时,findByAuthorityOrAuthority的Stub也未被调用,但异常提示第29行的Stub,说明本次运行触发的是getAdminRoleSet测试的检查逻辑。
解决办法
1. 拆分Stub到对应测试方法(推荐)
将每个测试方法需要的Stub单独写在方法内部,避免创建当前测试用不到的Stub,保持测试代码干净:
@ExtendWith(MockitoExtension.class) class RoleServiceImplTest { @InjectMocks RoleServiceImpl roleService; @Mock RoleDao roleDao; static final String USER = "USER"; static final String ADMIN = "ADMIN"; @Test void getRoleByName() { // 仅当前测试需要的Stub when(roleDao.findByAuthority(USER)).thenReturn(new Role(USER)); assertThat(roleService.getRoleByName(USER)).extracting(Role::getAuthority).isEqualTo(USER); verify(roleDao, times(1)).findByAuthority(USER); } @Test void getAdminRoleSet() { // 仅当前测试需要的Stub when(roleDao.findByAuthorityOrAuthority(USER, ADMIN)) .thenReturn(List.of(new Role(USER), new Role(ADMIN))); var adminRoles = roleService.getAdminRoleSet().stream() .map(Role::getAuthority) .toList(); assertThat(adminRoles.size()).isEqualTo(2); assertThat(adminRoles).asList().contains(ADMIN, USER); verify(roleDao, times(1)).findByAuthorityOrAuthority(USER, ADMIN); } }
2. 启用Lenient模式放宽检查
如果不想拆分Stub,可配置Mockito忽略未被使用的Stub:
方式一:给测试类添加全局宽松配置
@ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class RoleServiceImplTest { // 原代码保持不变 }
方式二:给单个Mock对象添加宽松注解
@Mock @Lenient RoleDao roleDao;
3. 修改全局Mockito配置(不推荐)
在项目的mockito-extensions/org.mockito.plugins.MockitoConfiguration文件中设置默认严格模式为LENIENT,但这种方式会关闭所有测试的严格检查,不利于维持测试代码质量,非必要不建议使用。
内容的提问来源于stack exchange,提问作者Sergey Zolotarev
相关产品推荐
相关产品推荐

