用途
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 版本、安装方式或与构建工具的强制绑定关系;上述内容均暂未提供。