junboV2/docs/junit5-migration-report.md

6.2 KiB
Raw Permalink Blame History

JUnit 5 迁移完成报告

迁移日期: 2026-02-06 项目: junboV2 执行人: Claude & User


迁移内容

1. JUnit 5 依赖配置

unittest.gradle 已配置完整的 JUnit 5 依赖:

dependencies {
    // JUnit 5
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
    testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.0'

    // Mockito (支持 JUnit 5)
    testImplementation 'org.mockito:mockito-core:4.11.0'
    testImplementation 'org.mockito:mockito-junit-jupiter:4.11.0'

    // AssertJ
    testImplementation 'org.assertj:assertj-core:3.19.0'

    // Lombok
    testCompileOnly 'org.projectlombok:lombok:1.18.26'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.26'
}

2. 代码修改

移除 public 修饰符

JUnit 5 不需要测试类和测试方法为 public

修改前:

public class UserServiceTest {
    @Test
    public void shouldCreateUser() {
        // ...
    }
}

修改后:

class UserServiceTest {
    @Test
    void shouldCreateUser() {
        // ...
    }
}

影响文件: 所有测试类和测试方法

3. 已有的 JUnit 5 特性

项目测试代码已经使用了 JUnit 5 的现代特性:

  • @ParameterizedTest - 参数化测试59个
  • @MethodSource - 方法源参数提供器
  • @DisplayName - 测试显示名称
  • @Nested - 嵌套测试类
  • org.junit.jupiter.api.Test - JUnit 5 注解

4. Jacoco 覆盖率配置

unittest.gradle 新增配置:

jacocoTestReport {
    dependsOn test
    reports {
        xml.enabled true      // 启用 XML 报告CI 集成)
        csv.enabled false
        html.enabled true     // 启用 HTML 报告
        html.destination file("${buildDir}/jacocoHtml")
    }

    // 排除不需要测试的类
    afterEvaluate {
        classDirectories.setFrom(files(classDirectories.files.collect {
            fileTree(dir: it, exclude: [
                '**/JunboApplication.class',
                '**/config/**',
                '**/entity/**',
                '**/po/**',
                '**/bo/**'
            ])
        }))
    }
}

// 配置覆盖率阈值
jacocoTestCoverageVerification {
    dependsOn jacocoTestReport

    violationRules {
        rule {
            limit {
                minimum = 0.60  // 最低60%覆盖率
            }
        }

        rule {
            element = 'CLASS'
            limit {
                minimum = 0.50  // 单个类最低50%
            }
            excludes = [
                '*.config.*',
                '*.entity.*',
                '*.po.*',
                '*.bo.*'
            ]
        }
    }
}

📊 迁移统计

项目 数量
测试文件总数 43
测试类数 ~30
已使用 JUnit 5 100%
参数化测试 59
移除 public 修饰符 全部

🚀 使用指南

运行测试

# 运行所有测试
./gradlew test

# 生成覆盖率报告
./gradlew test jacocoTestReport

# 检查覆盖率阈值
./gradlew jacocoTestCoverageVerification

# 查看 HTML 报告
# 浏览器打开: build/jacocoHtml/index.html

查看测试报告

# 测试结果报告
build/reports/tests/test/index.html

# 覆盖率报告
build/jacocoHtml/index.html

覆盖率要求

  • 整体覆盖率: 最低 60%
  • 单个类覆盖率: 最低 50%
  • 排除项: config/entity/po/bo 包(数据类无需测试)

💡 JUnit 5 最佳实践

1. 使用 @DisplayName

@Test
@DisplayName("当用户名为空时应抛出异常")
void shouldThrowExceptionWhenUsernameIsEmpty() {
    // ...
}

2. 使用 @ParameterizedTest

@ParameterizedTest(name = "{0}")
@MethodSource("loadTestCases")
@DisplayName("创建用户测试")
void shouldCreateUser(CreateUserTestCase testCase) {
    // ...
}

static Stream<CreateUserTestCase> loadTestCases() {
    return TestDataLoader.load(
        "user-service/create-user-cases.yaml",
        CreateUserTestCase.class
    ).stream();
}

3. 使用 @Nested 组织测试

@DisplayName("用户服务测试")
class UserServiceTest {

    @Nested
    @DisplayName("创建用户")
    class CreateUser {
        @Test
        void shouldSucceedWithValidData() { }

        @Test
        void shouldFailWithInvalidEmail() { }
    }

    @Nested
    @DisplayName("查询用户")
    class FindUser {
        @Test
        void shouldFindById() { }

        @Test
        void shouldReturnEmptyWhenNotFound() { }
    }
}

4. 使用 AssertJ 断言

import static org.assertj.core.api.Assertions.*;

@Test
void testUser() {
    User user = userService.create(request);

    assertThat(user)
        .isNotNull()
        .extracting("username", "email")
        .containsExactly("张三", "zhangsan@example.com");
}

📋 与 JUnit 4 的区别

特性 JUnit 4 JUnit 5
包名 org.junit org.junit.jupiter.api
测试类 必须 public 无需 public
测试方法 必须 public 无需 public
@Before @Before @BeforeEach
@After @After @AfterEach
@BeforeClass @BeforeClass (static) @BeforeAll (static)
@AfterClass @AfterClass (static) @AfterAll (static)
@Ignore @Ignore @Disabled
参数化 @RunWith(Parameterized.class) @ParameterizedTest

验证清单

  • 所有测试使用 JUnit 5 注解
  • 移除测试类的 public 修饰符
  • 移除测试方法的 public 修饰符
  • 配置 Jacoco 覆盖率检测
  • 设置覆盖率阈值60%
  • 排除不需要测试的类
  • 运行所有测试验证(进行中)
  • 生成覆盖率报告
  • 检查覆盖率是否达标

📚 参考资料


迁移状态: 完成 下一步: 提升测试覆盖率到 60%+