junboV2/docs/testing-toolkit-guide.md

6.7 KiB
Raw Permalink Blame History

测试工具包使用指南

junboV2 项目集成了测试工具包,提供 YAML 测试数据加载、通用断言等功能。

📦 功能组件

1. TestDataLoader - YAML 测试数据加载器

从 YAML 文件加载测试数据,支持类型安全和泛型。

2. BaseTestCase - 测试用例基类

提供测试用例的通用字段(名称、描述、跳过标记等)。

🚀 快速开始

步骤 1创建测试数据类

@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

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编写参数化测试

@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. 跳过测试

testCases:
  - name: "未实现的功能"
    skip: true
    skipReason: "等待后端 API 完成"
    input: {...}
    expected: {...}

在测试中检查:

@ParameterizedTest(name = "{0}")
@MethodSource("loadTestCases")
void testSomething(MyTestCase testCase) {
    if (testCase.isSkip()) {
        System.out.println("跳过: " + testCase.getSkipReason());
        return;
    }
    // 测试逻辑
}

🔧 配置

依赖已在 unittest.gradle 中配置:

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

🎯 运行测试

# 运行所有测试
./gradlew test

# 运行特定测试类
./gradlew test --tests "EmployeeRepositoryExampleTest"

# 查看测试报告
# 报告位置build/reports/tests/test/index.html

📖 参考资料


文档版本: 1.0 创建日期: 2026-02-06 维护者: junboV2 团队