164 lines
3.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 <repository-url>
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<User> 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/)