junboV2/docs/testing-toolkit-guide.md

260 lines
6.7 KiB
Markdown
Raw 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.

# 测试工具包使用指南
junboV2 项目集成了测试工具包,提供 YAML 测试数据加载、通用断言等功能。
## 📦 功能组件
### 1. TestDataLoader - YAML 测试数据加载器
从 YAML 文件加载测试数据,支持类型安全和泛型。
### 2. BaseTestCase - 测试用例基类
提供测试用例的通用字段(名称、描述、跳过标记等)。
## 🚀 快速开始
### 步骤 1创建测试数据类
```java
@Data
@EqualsAndHashCode(callSuper = true)
public class CreateUserTestCase extends BaseTestCase {
private Input input;
private Expected expected;
@Data
public static class Input {
private String username;
private String email;
private Integer age;
}
@Data
public static class Expected {
private boolean success;
private String errorCode;
}
}
```
### 步骤 2创建 YAML 测试数据
文件位置:`src/test/resources/testdata/user-service/create-user-cases.yaml`
```yaml
testCases:
- name: "正常创建用户"
description: "使用有效数据创建用户"
input:
username: "张三"
email: "zhangsan@example.com"
age: 25
expected:
success: true
- name: "用户名为空"
description: "用户名为空应该失败"
input:
username: ""
email: "test@example.com"
age: 25
expected:
success: false
errorCode: "INVALID_USERNAME"
- name: "邮箱格式错误"
description: "邮箱格式不正确应该失败"
input:
username: "李四"
email: "invalid-email"
age: 25
expected:
success: false
errorCode: "INVALID_EMAIL"
- name: "年龄为负数"
description: "年龄为负数应该被拒绝"
skip: true
skipReason: "暂未实现年龄验证"
input:
username: "王五"
email: "wangwu@example.com"
age: -1
expected:
success: false
errorCode: "INVALID_AGE"
```
### 步骤 3编写参数化测试
```java
@SpringBootTest
@DisplayName("用户服务测试")
class UserServiceTest {
@Autowired
private UserService userService;
@ParameterizedTest(name = "{0}")
@MethodSource("loadCreateUserCases")
@DisplayName("创建用户")
void shouldCreateUser(CreateUserTestCase testCase) {
// 跳过标记的测试用例
if (testCase.isSkip()) {
System.out.println("跳过: " + testCase.getSkipReason());
return;
}
// Given
CreateUserRequest request = new CreateUserRequest();
request.setUsername(testCase.getInput().getUsername());
request.setEmail(testCase.getInput().getEmail());
request.setAge(testCase.getInput().getAge());
// When & Then
if (testCase.getExpected().isSuccess()) {
User user = userService.createUser(request);
assertThat(user).isNotNull();
assertThat(user.getUsername()).isEqualTo(request.getUsername());
} else {
assertThatThrownBy(() -> userService.createUser(request))
.isInstanceOf(BusinessException.class)
.hasMessageContaining(testCase.getExpected().getErrorCode());
}
}
static Stream<CreateUserTestCase> loadCreateUserCases() {
return TestDataLoader.load(
"user-service/create-user-cases.yaml",
CreateUserTestCase.class
).stream();
}
}
```
## 📁 目录结构
```
src/test/
├── java/info/panli/junbo/
│ ├── test/support/ # 测试工具包
│ │ ├── TestDataLoader.java # YAML 数据加载器
│ │ └── BaseTestCase.java # 测试用例基类
│ │
│ ├── organization/ # 组织领域测试
│ │ └── repository/
│ │ └── EmployeeRepositoryExampleTest.java
│ │
│ ├── attendance/ # 考勤领域测试
│ ├── scrum/ # 迭代领域测试
│ ├── kpa/ # KPA 领域测试
│ └── incentive/ # 激励领域测试
└── resources/
└── testdata/ # 测试数据目录
├── organization/
│ └── find-by-name-cases.yaml
├── attendance/
├── scrum/
├── kpa/
└── incentive/
```
## 💡 最佳实践
### 1. YAML 文件命名
- 使用小写字母和连字符
- 格式:`{功能}-cases.yaml`
- 示例:`create-user-cases.yaml`, `find-by-id-cases.yaml`
### 2. 测试用例命名
- 使用 `@DisplayName` 提供中文描述
- `@ParameterizedTest` 的 name 使用 `"{0}"` 显示测试用例名称
### 3. 测试数据组织
- 按领域划分目录:`testdata/organization/`, `testdata/attendance/`
- 每个服务/Repository 一个子目录
- 相关测试用例放在同一个 YAML 文件
### 4. 跳过测试
```yaml
testCases:
- name: "未实现的功能"
skip: true
skipReason: "等待后端 API 完成"
input: {...}
expected: {...}
```
在测试中检查:
```java
@ParameterizedTest(name = "{0}")
@MethodSource("loadTestCases")
void testSomething(MyTestCase testCase) {
if (testCase.isSkip()) {
System.out.println("跳过: " + testCase.getSkipReason());
return;
}
// 测试逻辑
}
```
## 🔧 配置
依赖已在 `unittest.gradle` 中配置:
```groovy
dependencies {
// JUnit 5 + Mockito + AssertJ
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0'
testImplementation 'org.mockito:mockito-core:4.11.0'
testImplementation 'org.assertj:assertj-core:3.19.0'
// YAML 支持
testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.15.2'
testImplementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.2'
// Spring Boot Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
```
## 📚 示例代码
查看以下示例了解如何使用:
1. **Repository 测试**`EmployeeRepositoryExampleTest.java`
2. **YAML 数据**`testdata/organization/find-by-name-cases.yaml`
## 🎯 运行测试
```bash
# 运行所有测试
./gradlew test
# 运行特定测试类
./gradlew test --tests "EmployeeRepositoryExampleTest"
# 查看测试报告
# 报告位置build/reports/tests/test/index.html
```
## 📖 参考资料
- JUnit 5: https://junit.org/junit5/docs/current/user-guide/
- AssertJ: https://assertj.github.io/doc/
- Mockito: https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
- Spring Boot Test: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.testing
---
**文档版本**: 1.0
**创建日期**: 2026-02-06
**维护者**: junboV2 团队