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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 19:15:05