单元测试service层应只测业务逻辑,不启动spring容器、不连数据库,用mockito隔离依赖;需配置spring-boot-starter-test,用@extendwith(mockitoextension.class)、@mock和@injectmocks,聚焦输入→行为→输出验证。

直接对 Service 层做单元测试,核心是**只测业务逻辑,不启动 Spring 容器,也不连数据库或外部服务**。关键在于用 Mockito 隔离依赖,比如 UserRepository、其他 Service 或工具类,让被测 UserService 只专注自己的分支判断、数据转换、异常抛出等行为。
需要先加好测试依赖
确保 pom.xml 中有:
-
spring-boot-starter-test(它已包含 JUnit 5、Mockito、AssertJ 等) - 如果项目还在用 JUnit 4,额外加
junit:junit:4.13.2,但新项目建议统一用 JUnit 5 - 不需要单独引
mockito-core,starter 已自带
用 @ExtendWith(MockitoExtension.class) 启动 Mock 支持
这是 JUnit 5 的标准写法,替代旧版的 @RunWith(MockitoJUnitRunner.class):
-
@Mock标注要模拟的依赖对象(如UserRepository) -
@InjectMocks标注被测的 Service 实例(如UserService),框架会自动把@Mock对象注入进去 - 不用手写
new UserService(),也不用@Autowired—— 那样会走 Spring 容器,就不是单元测试了
测试重点放在“输入→行为→输出”三要素
每个测试方法应清晰表达一个业务场景:
- 准备输入:构造参数和 mock 返回值(例如
when(repo.findById(1L)).thenReturn(Optional.of(user))) - 执行动作:调用被测方法(例如
userService.getUserById(1L)) - 验证结果:
- 返回值是否符合预期(
assertEquals("admin", result.getUsername())) - 是否调用了指定依赖(
verify(repo, times(1)).findById(1L)) - 是否抛出正确异常(
assertThrows<usernotfoundexception> { userService.getUserById(999L) }</usernotfoundexception>)
- 返回值是否符合预期(
避开常见误区
这些做法会让测试变慢、变脆,失去单元测试意义:
- 别在 Service 测试里加
@SpringBootTest—— 它会加载整个上下文,属于集成测试范畴 - 别在单元测试里读
application.yml或用@Value注入配置 —— 应该把配置项抽成参数或常量,便于 mock 和替换 - 别测试空实现、getter/setter 或纯委托方法(如
return repo.save(u))—— 没有业务逻辑,不值得覆盖 - 避免过度 mock:比如 mock 一个工具类的静态方法,优先考虑重构为可注入的 Bean
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











