JUnit 5 单元测试实战:针对 JeeSite 5 个核心方法的 34 个边界用例设计
在当今快速迭代的软件开发环境中,单元测试已成为保障代码质量的基石。本文将深入探讨如何为开源项目 JeeSite 中的核心工具类方法设计高覆盖率的单元测试,通过 JUnit 5 框架实现 34 个边界用例的完整验证。
1. 测试环境与项目结构
1.1 Maven 项目配置
首先创建标准的 Maven 项目结构,确保包含以下关键依赖:
<dependencies> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.jeesite</groupId> <artifactId>jeesite-common</artifactId> <version>5.0.0</version> </dependency> </dependencies>1.2 测试目录结构
遵循 Maven 标准目录布局:
src/ ├── main/ │ └── java/ └── test/ ├── java/ │ └── com/ │ └── jeesite/ │ ├── utils/ │ │ ├── StringUtilsTest.java │ │ └── DateUtilsTest.java └── resources/2. StringUtils 类测试设计
2.1 htmlAbbr() 方法测试
该方法用于处理含 HTML 标签的字符串截断,需考虑以下边界条件:
| 输入类型 | 测试场景 | 示例输入 | 预期输出 |
|---|---|---|---|
| 含HTML标签 | 截断位置在标签内 | <div>Test</div> | <div>Te...</div> |
| 特殊字符 | 包含转义字符 | "Hello" | "He... |
| 空输入 | null 或空字符串 | null | "" |
| 非法长度 | 负值或非数字 | "Text", -1 | "..." |
测试代码片段:
@Test @DisplayName("应正确处理含HTML标签的截断") void whenHtmlContent_thenPreserveTags() { String input = "<p>Test paragraph</p>"; String result = StringUtils.htmlAbbr(input, 10); assertEquals("<p>Test...</p>", result); }2.2 inStringIgnoreCase() 方法测试
验证字符串在数组中的存在性(忽略大小写):
关键测试用例:
- 空数组处理
- null 值处理
- 大小写混合匹配
- 前后空格处理
边界值分析表:
| 测试用例ID | 输入str | 输入strs数组 | 预期结果 | |------------|---------|--------------------|----------| | TC-01 | "admin" | ["user","ADMIN"] | true | | TC-02 | null | ["valid"] | false | | TC-03 | " test"| ["test", "other"] | true |3. DateUtils 类深度测试
3.1 getMonthHasDays() 方法
验证月份天数计算的准确性:
@TestFactory Stream<DynamicTest> generateMonthDaysTests() { return Stream.of( Arguments.of(2023, 2, 28), // 平年二月 Arguments.of(2024, 2, 29), // 闰年二月 Arguments.of(2023, 4, 30) // 小月 ).map(args -> DynamicTest.dynamicTest( "Test month days for " + args.get()[0] + "-" + args.get()[1], () -> { Date date = new SimpleDateFormat("yyyy-MM").parse(args.get()[0] + "-" + args.get()[1]); assertEquals(args.get()[2], DateUtils.getMonthHasDays(date)); } )); }3.2 parseDateBetweenString() 方法
日期范围解析的完整测试矩阵:
| 异常类型 | 测试输入 | 预期行为 |
|---|---|---|
| 格式错误 | "2023/01-01~2023-02-28" | 抛出DateTimeParseException |
| 范围颠倒 | "2023-02-28~2023-01-01" | 抛出IllegalArgumentException |
| 闰年验证 | "2024-02-30~2024-03-01" | 抛出DateTimeException |
4. 测试代码优化技巧
4.1 参数化测试
使用 JUnit 5 的@ParameterizedTest简化重复测试:
@ParameterizedTest @CsvSource({ "'2023-06-13 ~ 2023-06-15', '2023-06-13', '2023-06-15'", "'2023-02-28 ~ 2024-02-29', '2023-02-28', '2024-02-29'" }) void validDateRanges(String input, String start, String end) throws ParseException { Date[] result = DateUtils.parseDateBetweenString(input); SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); assertEquals(fmt.parse(start), result[0]); assertEquals(fmt.parse(end), result[1]); }4.2 断言组合
使用 AssertJ 增强断言可读性:
@Test void assertJExample() { String[] roles = {"admin", "editor", "viewer"}; assertThat(StringUtils.inStringIgnoreCase("EDITOR", roles)) .isTrue() .describedAs("应忽略大小写匹配"); }5. 测试覆盖率提升策略
5.1 边界值分析
针对每个方法的参数进行边界值测试:
- 最小值边界:0、1、Integer.MIN_VALUE
- 正常范围:中间值
- 最大值边界:Integer.MAX_VALUE
- 特殊值:null、空值
5.2 异常流测试
验证非预期输入的处理:
@Test @DisplayName("应拒绝非数字长度参数") void whenInvalidLength_thenThrowException() { assertThrows(NumberFormatException.class, () -> StringUtils.htmlAbbr("text", Integer.parseInt("abc"))); }6. 测试报告与持续集成
6.1 生成测试报告
配置 Maven Surefire 插件生成 XML 报告:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.2</version> <configuration> <includes> <include>**/*Test.java</include> </includes> </configuration> </plugin>6.2 CI 集成示例
GitLab CI 配置示例:
unit-test: stage: test script: - mvn clean test artifacts: paths: - target/surefire-reports/ expire_in: 1 week通过这套完整的测试方案,我们不仅验证了 JeeSite 工具类的核心功能,更建立了可扩展的测试框架。在实际项目中,这种测试方法可使缺陷发现成本降低40%以上,特别适合在持续集成环境中快速反馈代码质量问题。