Spring Boot服务实现单元测试问题求助(Mockito+JUnit5)
问题分析与解决方案
方案一的核心问题
- Mock了服务本身:用
@MockBean标注EntityService,等于把Spring容器里的服务替换成了Mock对象。调用entityService.serviceDeleteEntity(1L)时,执行的是Mock的空实现,根本不会去调用你Mock的仓库方法,自然和仓库没关联。 - 冗余操作与错误修饰符:
@Mock标注的Entity e完全没必要,你后面直接new了实体;而且测试方法simpleTest是private的,JUnit5根本识别不了这个测试方法,不会执行。 - Mock仓库的save操作无效:Mock对象的方法默认无任何行为,调用
entityRepository.save(e)不会真的把实体存到“仓库”里,Mock仓库不会保留数据。
方案二的潜在问题
方案二的思路是对的:用@MockBean替换容器里的仓库,注入真实的服务实现类来测试。但可能踩这些坑:
- 服务实现类未被Spring管理:如果
EntityServiceImpl没加@Service或@Component,@Autowired注入entityService会报错。 - 服务逻辑依赖其他仓库方法:如果
serviceDeleteEntity里先执行了findById之类的查询操作,只MockspecialDeleteEntity的返回值的话,查询会返回null,可能导致服务逻辑抛出异常或返回不符合预期的结果。 - 没必要启动完整Spring上下文:
@SpringBootTest会加载整个应用上下文,测试服务层用这个太重,推荐用Mockito的轻量测试方式。
正确的测试写法
方式一:轻量单元测试(不启动Spring上下文)
这种方式只测试服务实现类的逻辑,不需要加载Spring容器,速度更快:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) public class EntityServiceImplTest { // Mock仓库 @Mock private EntityRepository entityRepository; // 把Mock的仓库注入到真实的服务实现类中 @InjectMocks private EntityServiceImpl entityService; // 这里用具体的实现类,不是接口 @Test void testServiceDeleteEntity() { // 1. 模拟仓库方法的返回值 when(entityRepository.specialDeleteEntity(1L)).thenReturn(1); // 如果服务里需要先查询实体,还要模拟查询 // Entity testEntity = new Entity(); // testEntity.setName("abc"); // when(entityRepository.findById(1L)).thenReturn(Optional.of(testEntity)); // 2. 调用服务方法 int result = entityService.serviceDeleteEntity(1L); // 3. 断言结果 assertEquals(1, result); // 4. 验证仓库方法是否被正确调用 verify(entityRepository, times(1)).specialDeleteEntity(1L); } }
方式二:集成测试(启动Spring上下文)
如果需要测试Spring的依赖注入、事务等容器相关逻辑,用@SpringBootTest:
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import static org.mockito.Mockito.*; import static org.junit.jupiter.api.Assertions.*; @SpringBootTest class EntityServiceImplTest { @MockBean private EntityRepository entityRepository; @Autowired private EntityService entityService; // 这里可以用接口,Spring会注入真实的实现类 @Test void testDelete() { // 模拟仓库方法 when(entityRepository.specialDeleteEntity(1L)).thenReturn(1); // 若服务逻辑需要查询,补充模拟 // Entity entity = new Entity(); // entity.setName("abc"); // when(entityRepository.findById(1L)).thenReturn(Optional.of(entity)); // 调用服务 int result = entityService.serviceDeleteEntity(1L); // 断言 assertEquals(1, result); // 验证仓库方法被调用 verify(entityRepository).specialDeleteEntity(1L); } }
关键注意点
- 测试服务层时,不要Mock服务本身,要Mock它依赖的仓库,然后用真实的服务实现类来测试。
@InjectMocks会自动把标注@Mock的依赖注入到目标对象中,适合纯单元测试。- 如果用
@SpringBootTest,确保服务实现类被Spring扫描到(加@Service),@MockBean会替换容器中原有的仓库Bean。
内容的提问来源于stack exchange,提问作者FrenchCornichon
相关产品推荐
相关产品推荐

