SuperAPI AI 工具集

test-quality

测试与质量

查看项目

更新于 10-15 14:30

技能介绍

用途

Test Quality 是一个面向 Java 项目的测试编写技能,目标是用现代最佳实践编写高质量、可维护的测试。它适用于以下场景:编写新的测试类;审查或改进现有测试;用户要求“添加测试”或“提升测试覆盖率”;代码审查中提到缺少测试。

主要能力

该技能以 JUnit 5(Jupiter)和 AssertJ 为核心框架,提供从断言、结构、命名到组织方式的完整指导。

框架偏好

使用 JUnit 5(Jupiter)的注解,例如 @Test、@DisplayName、@BeforeEach、@Nested,并静态导入 AssertJ 的 Assertions.*。

优先使用 AssertJ 而非标准 JUnit 断言。示例:

assertThat(plugin.getState())
    .as("Plugin should be started after initialization")
    .isEqualTo(PluginState.STARTED);

assertThat(plugins)
    .hasSize(3)
    .extracting(Plugin::getId)
    .containsExactly("plugin1", "plugin2", "plugin3");

应避免可读性较差、失败信息不够描述性的 JUnit 断言,例如 assertEquals(...) 或 assertTrue(plugins.size() == 3)。

测试结构(AAA 模式)

始终使用 Arrange-Act-Assert 模式:先准备测试数据与依赖,再执行被测行为,最后验证结果。示例:

@Test
@DisplayName("Should load plugin from valid directory")
void shouldLoadPluginFromValidDirectory() {
    // Arrange - Setup test data and dependencies
    Path pluginDir = Paths.get("test-plugins/valid-plugin");
    PluginLoader loader = new DefaultPluginLoader();

    // Act - Execute the behavior being tested
    Plugin plugin = loader.load(pluginDir);

    // Assert - Verify results
    assertThat(plugin)
        .isNotNull()
        .extracting(Plugin::getId, Plugin::getVersion)
        .containsExactly("test-plugin", "1.0.0");
}

命名约定

测试类名推荐 PluginManagerTest(简单标准)或 PluginManagerShould(BDD 风格,若团队偏好),避免 TestPluginManager。

测试方法名有两种选项:一是 should_expectedBehavior_when_condition 描述式,例如 should_throwException_when_pluginDirectoryNotFound;二是自然语言配合 @DisplayName,例如 @DisplayName("Should load all plugins from directory") 搭配简洁方法名。

AssertJ 强大特性

集合断言:支持 isNotEmpty()、hasSize()、doesNotContainNull(),以及 filteredOn、extracting、containsExactlyInAnyOrder、allMatch 等高级过滤与提取。

异常断言:使用 assertThatThrownBy 或 assertThatExceptionOfType 验证异常类型、消息内容、消息正则、原因链等。

对象断言:可通过 extracting 提取多个属性并验证,或使用 usingRecursiveComparison() 进行逐字段比较。

软断言:使用 SoftAssertions 在一次测试中执行多个检查,最后调用 softly.assertAll(),即使部分失败也会评估全部断言。

字符串断言:支持 startsWith、contains、doesNotContain、matches、hasLineCount 等。

测试组织

使用 @Nested 与 @DisplayName 对测试进行逻辑分组,例如按“启动插件时”“停止插件时”划分。

参数化测试:使用 @ParameterizedTest 配合 @ValueSource、@CsvSource、@MethodSource 等,覆盖多组输入。

常见模式

Mockito 模拟:使用 @ExtendWith(MockitoExtension.class)、@Mock、@InjectMocks,配合 when(...).thenReturn(...) 与 verify(...)。

测试夹具:使用 @BeforeEach 初始化临时目录、配置与测试对象,使用 @AfterEach 清理资源。

异步操作测试:使用 CompletableFuture 配合 AssertJ 的 succeedsWithin(Duration) 与 satisfies 验证异步结果。

Token 优化

建议先生成测试骨架(以注释列出测试用例),再逐个实现、每次验证编译并运行测试,最后按需重构;同时抽取公共设置到辅助方法中复用。

使用方式

该技能以指导性内容形式提供,在编写或审查 Java 测试时应用上述规范。资料中未提供独立的安装命令或调用命令。

覆盖率指南

目标:核心逻辑行覆盖率 80%+。重点关注业务逻辑、复杂算法、边界情况;跳过简单的 getter/setter、POJO 与生成代码。测试应覆盖正常路径、错误条件与边界情况。

高优先级测试对象:公共 API、复杂业务逻辑、错误处理、边界与临界情况、集成点。低优先级:简单 getter/setter、无逻辑的简单 POJO。

反模式

应避免:通用测试名(如 test1、testPlugin);测试实现细节(耦合内部状态);依赖时间戳的脆弱断言;一个测试中混入大量不相关断言;吞掉异常细节的写法。

推荐写法示例:

@Test
@DisplayName("Should reject plugin with missing dependencies")
void shouldRejectPluginWithMissingDependencies() {
    PluginDescriptor descriptor = PluginDescriptor.builder()
        .id("test-plugin")
        .dependencies(List.of("missing-dep"))
        .build();

    assertThatThrownBy(() -> manager.load(descriptor))
        .isInstanceOf(PluginException.class)
        .hasMessageContaining("Missing dependencies: missing-dep");
}

与覆盖率工具集成

提供 Maven 中 JaCoCo 插件的配置示例(版本 0.8.11),包含 prepare-agent 与 report 两个执行目标。生成测试后建议运行:

# Run tests with coverage
mvn clean test jacoco:report

# View coverage report
open target/site/jacoco/index.html

# Check coverage threshold
mvn verify # Fails if below configured threshold

快速参考

资料提供基础断言、集合、字符串、异常、自定义描述、对象比较等常用 AssertJ 写法的速查清单。

最佳实践总结

使用 AssertJ 进行所有断言;遵循 AAA 模式;使用 @DisplayName 提供描述性名称;每个测试聚焦一个概念;测试行为而非实现;抽取公共设置辅助方法;使用 @Nested 逻辑分组;参数化相似测试;多检查时使用软断言;覆盖率聚焦业务逻辑而非样板代码。

限制

资料未说明该技能的具体运行环境要求、支持的 Java 版本、安装方式或与构建工具的强制绑定关系;上述内容均暂未提供。