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

