# Spring Boot 单元测试学习示例(基于JDBC) 这是一个完整的Spring Boot单元测试学习项目,演示了如何使用JUnit 5和Mockito编写高质量的单元测试。 项目使用纯JDBC方式(非JPA),更贴近传统的数据库操作方式。 ## 项目结构 ``` src/ ├── main/java/com/example/usertest/ │ ├── entity/ # 实体类 │ ├── dto/ # 数据传输对象 │ ├── repository/ # 数据访问层接口 │ │ ├── UserRepository.java # Repository接口 │ │ └── JdbcUserRepository.java # JDBC实现示例 │ ├── service/ # 业务逻辑层 │ ├── exception/ # 自定义异常 │ └── config/ # 配置类 ├── main/resources/ │ ├── application.properties # 应用配置 │ └── schema.sql # 数据库表结构(仅供参考) └── test/java/com/example/usertest/ └── service/ # 单元测试类 ``` ## 技术栈 - **Spring Boot 3.1.5** - **Java 17** - **JUnit 5** - 测试框架 - **Mockito** - Mock框架 - **AssertJ** - 断言库 - **Maven** - 构建工具 ## 核心功能 UserService提供以下业务功能: 1. **用户注册** (`registerUser`) 2. **用户登录** (`loginUser`) 3. **修改密码** (`changePassword`) 4. **删除用户** (`deleteUser`) 5. **获取用户信息** (`getUserById`) ## 测试覆盖 单元测试覆盖了以下场景: ### 正常场景测试 - 成功注册新用户 - 成功登录 - 成功修改密码 - 成功删除用户 - 成功获取用户信息 ### 异常场景测试 - 用户名/邮箱已存在 - 用户不存在 - 密码错误 - 输入验证失败 - 空值处理 ### 边界条件测试 - 用户名长度边界(3-20字符) - 密码长度边界(6-20字符) - 特殊字符处理 ### Mock验证测试 - 方法调用次数验证 - 方法调用顺序验证 - 参数验证 ## 如何运行 ### 1. 克隆项目 ```bash git clone cd springboot-test ``` ### 2. 运行所有测试 ```bash mvn test ``` ### 3. 运行特定测试 ```bash # 运行特定测试类 mvn test -Dtest=UserServiceTest # 运行特定测试方法 mvn test -Dtest=UserServiceTest#registerUser_Success ``` ### 4. 生成测试报告 ```bash mvn surefire-report:report ``` ## 关键学习点 ### 1. 测试注解 - `@ExtendWith(MockitoExtension.class)` - 启用Mockito - `@Mock` - 创建模拟对象 - `@InjectMocks` - 注入依赖 - `@Nested` - 组织测试 - `@DisplayName` - 测试描述 ### 2. 测试模式 ```java @Test void testMethod() { // Given - 准备测试数据 // When - 执行测试 // Then - 验证结果 } ``` ### 3. Mock使用 ```java // 定义行为 when(repository.findById(1L)).thenReturn(Optional.of(user)); // 验证调用 verify(repository).save(any(User.class)); // 捕获参数 ArgumentCaptor captor = ArgumentCaptor.forClass(User.class); verify(repository).save(captor.capture()); ``` ### 4. 断言示例 ```java // 基本断言 assertThat(result).isNotNull(); assertThat(result.getUsername()).isEqualTo("testuser"); // 异常断言 assertThatThrownBy(() -> service.method()) .isInstanceOf(ValidationException.class) .hasMessage("Error message"); ``` ## 最佳实践 1. **单一职责**: 每个测试只验证一个功能点 2. **独立性**: 测试之间相互独立 3. **可读性**: 使用描述性的测试名称 4. **完整性**: 覆盖正常和异常场景 5. **可维护性**: 使用常量避免硬编码 ## 扩展学习 - 集成测试 (`@SpringBootTest`) - 数据库测试 (`@DataJpaTest`) - Web层测试 (`@WebMvcTest`) - 测试容器 (Testcontainers) - 性能测试 (JMH) ## 参考资源 - [Spring Boot Testing](https://spring.io/guides/gs/testing-web/) - [JUnit 5 User Guide](https://junit.org/junit5/docs/current/user-guide/) - [Mockito Documentation](https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html) - [AssertJ Documentation](https://assertj.github.io/doc/)