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

注入Mock的实例由Mockito还是Spring创建?测试疑问

RoleServiceImpl实现与测试疑问解答

实现代码

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"));
    }
}

测试代码

@SpringBootTest
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);
    }
}

疑问解答

1. RoleServiceImpl实例的获取逻辑

你观察的没错,这个实例是Mockito通过new创建的,并非Spring容器提供的Bean。加@SpringBootTest后不会空指针的原因是:
@SpringBootTest默认启用了SpringExtension,这个扩展整合了Mockito的功能,会自动处理测试类中的@Mock和@InjectMocks注解:

  • 先为@Mock标注的roleDao创建模拟实例;
  • 再通过RoleServiceImpl的构造函数,把模拟的roleDao注入进去,完成roleService的实例化。
    如果移除@SpringBootTest,测试类不会触发任何扩展处理,roleService和roleDao都会是null,调用方法自然抛出空指针异常。

2. @SpringBootTest是否隐式自动装配测试字段?

不是@SpringBootTest直接做的自动装配,而是它整合的测试扩展在起作用:

  • @SpringBootTest核心是启动Spring上下文,但这个测试里的roleService并不是从Spring容器获取的Bean——@InjectMocks是Mockito的注解,负责实例化目标对象并注入@Mock依赖;
  • 要是去掉@InjectMocks,@SpringBootTest才会自动从Spring容器装配RoleServiceImpl的Bean,但此时roleDao也会是容器中的真实Bean,就失去了Mock的意义。
    简单说,这里的字段注入是Mockito的行为,@SpringBootTest只是提供了让Mockito注解生效的测试环境。

内容的提问来源于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:01:27