文章目录
单元测试理念
单元测试的基本原则
宏观上,符合AIR原则
- A: Automatic(自动化)
- 需全自动执行,不需要人工介入检查
- 不允许使用System.out进行人工验证,用断言
- I:Independent(独立性)
- 测试用例不允许互相调用
- 不允许出现执行次序的依赖
- R:Repeatable(可重复)
- 每次代码修改后,可以重复利用测试用例
微观上,符合BCDE原则
- B:Border,边界测试
- 包括循环边界,特殊取值,特殊时间点,数据顺序
- C:Correct,正确
- 正确的输入,得到预期输入
- D:Design
- 与设计文档相结合
- E: Error
- 进行一下错误输入检测预期的错误结果
单元测试覆盖率
粗粒度覆盖率
- 类覆盖
- 方法覆盖
细粒度覆盖率
- 行覆盖
- 行覆盖率的计算公式的分子是执行到的语句数,分母是总的可执行语句
- 分支覆盖率
- 条件判定覆盖
- 条件组合覆盖
- 路径覆盖
Junit5系列
介绍
- JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
- JUnit Platform 作为在JVM上启动测试框架的基础。
- JUnit Jupiter 是新的编程模型和扩展模型的组合,JUnit 5中类都在此包中
- Unit Vintage 为在平台上运行基于JUnit 3和JUnit 4的测试提供了一个测试引擎。
Maven导入Junit5
<dependencies>
<!--导入Junit5依赖-->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<!--参数化测试需要添加==junit-jupiter-params== 模块-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
注解
-
@Test
- Junit框架会在测试阶段自动找出所有使用该注解表明的方法并运行。
@Test void myFirstTest() { Assertions.assertEquals(11,12); } -
@DisplayName
- 定义测试方法和类的名称。
- 测试类和测试方法可以声明自定义显示名称,将由测试运行器和测试报告显示。
@DisplayName("测试类名") public class DisplayNameTest { @Test @DisplayName("测试方法") void testWithDisplayNameContainingSpaces() { } } -
@Disabled
- 注明一个测试类或方法不再运行
//整个类不再运行 @Disabled class DisabledClassDemo { 。。。 } //单个方法不再运行 class DisabledTestsDemo { @Disabled @Test void testWillBeSkipped() { } } -
@BeforeEach
- 在每一个测试方法运行之前,运行指定的方法
-
@AfterEach
- 在每一个测试方法运行之后,运行指定的方法
-
@BeforeAll
- 在每一个测试类运行之前,都运行一个指定的方法
-
@AfterAll
- 在每一个测试类运行之后,都运行一个指定的方法
-
@Nested
- 为测试添加嵌套层级
-
@Tag
- 为测试类或方法添加标签,以便有选择性的运行。
-
- 参数化测试,作用类似于@Test,但可以让一个方法使用不同的入参运行多次。
- 必须声明至少一个参数源,该源将为每个调用提供参数。
- @ValueSource
- @EnumSource
- @MethodSource
- @ArgumentsSource
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.assertj.core.api.Assertions.*; public class DemoApplicationTests { @ParameterizedTest @DisplayName("测试方法") @CsvSource({ "1, 2, 3", "2 ,3, 4", "2, 5, 100" }) void myFirstTest(int a, int b, int c) { assertThat(a).isLessThan(3); } }
断言与假设
普通断言
- 常用的断言被封装在org.junit.jupiter.api.Assertions类中 ,均为静态方法。
| 方法 | 释义 |
|---|---|
| Assertions.fail() | 运行到这句表示测试失败 |
| Assertions.assertTrue(a == 100) / Assertions.assertFalse(…) | 断言条件为真或为假 |
| Assertions.assertNull(object) / Assertions.assertNotNull(object) | 断言对象为空/非空 |
| Assertions.assertEquals(…) / Assertions.assertNotEquals(…); | 指定值是否相等,基本数据类型比较值,对象会调用equals()方法比较 |
| Assertions.assertArrayEquals() | 断言数组元素全部相等 |
| Assertions.assertSame(); / Assertions.assertNotSame(); | 两个对象是否为同一个对象 |
| Assertions.assertThrows() / Assertions.assertDoseNotThrow() | 断言是否抛出一个特定类型异常 |
| Assertions.assertTimeout() | 断言超时,超时后继续执行 |
| Assertions.assertIterableEquals(); | 断言迭代器中的元素全部相等 |
| Assertions.assertLinesMatch(); | 断言字符串列表全部相等 |
| Assertions.assertAll(); | 多个条件同时满足 |
普通假设
- 假设都是在 org.junit.jupiter.api.Assumptions 的静态方法。
- 假设与断言类似,只不过当条件不满足时,测试会直接退出而不是认定为测试失败,最终记录的状态为跳过。
| 方法 | 释义 |
|---|---|
| assumeTrue / assumeFalse | 判定真假 |
AssertJ流式断言-第三方断言库
- 加入依赖
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
<version>3.9.1</version>
</dependency>
- 流式测试
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
@DisplayName("测试类名")
public class DemoApplicationTests {
@Test
@DisplayName("测试方法")
void myFirstTest(int a) {
assertThat("test").isNotBlank() // 是否为" "字符串
.as("字符串断言描述").isSubstringOf("test1") // 是否为test1的一部分
.isSameAs("test") // 对象内元素是否相等
.isNotEmpty() // 是否为空字符串
.isEqualTo("test") // 是否相等
.isEqualToIgnoringCase("Test") // 是否相等(忽略大小写)
.isExactlyInstanceOf(String.class) // 是否是实例
.isIn(Arrays.asList("test", "hello")) // 是否在列表中
.isIn("test", "hello") // 是否在参数列表中
.isInstanceOfAny(String.class, Integer.class) // 是否是实例中任何一个
.isNotNull() // 是否不为空
.contains("es") // 是否包含es子串
.startsWith("te") // te开始
.endsWith("st") // st结束
.matches(".e.t"); // 是否匹配 .e.t 格式
assertThat("").isNullOrEmpty();
}
}
本文介绍了单元测试的核心原则AIR和BCDE,并详细讲解了Junit5的测试覆盖率、注解使用以及断言和假设。通过Junit5的@Nested、@Tag等注解实现更灵活的测试组织,使用AssertJ增强断言能力,提高测试质量。

1133

被折叠的 条评论
为什么被折叠?



