Java 第三方包-测试框架

1. JUnit5

1. wiki

Junit5 由来自三个不同子项目组成,JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform : JVM 上启动测试框架的基础,不仅支持 Junit 自制的测试引擎,其他测试引擎也都可以接入。
  • JUnit Jupiter : 提供了新的编程模型,是新特性的核心。内部包含了一个测试引擎,用于在 Junit Platform 上运行。
  • JUnit Vintage : 由于 JUint 已经发展多年,为了照顾老的项目,JUnit Vintage 提供了兼容 JUnit4.x,Junit3.x 的测试引擎。

执行流程为

junit5_01

2. 引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<properties>
<junit-platform.version>1.5.2</junit-platform.version>
<junit.version>1.5.2</junit.version>
</properties>

<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<version>${junit-platform.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

3. 常用注解

  • @Test : 表示方法是测试方法。但是与 JUnit4 的 @Test 不同,他的职责非常单一不能声明任何属性,拓展的测试将会由 Jupiter 提供额外测试
  • @DisplayName : 为测试类或者测试方法设置展示名称
  • @BeforeAll : 表示在所有单元测试之前执行
  • @AfterAll : 表示在所有单元测试之后执行
  • @BeforeEach : 表示在每个单元测试之前执行
  • @AfterEach : 表示在每个单元测试之后执行
  • @Disabled : 表示测试类或测试方法不执行,类似于JUnit4中的 @Ignore
  • @Tag : 表示单元测试类别,类似于 JUnit4 中的 @Categories
  • @Timeout : 表示测试方法运行如果超过了指定时间将会返回错误
  • @ExtendWith : 为测试类或测试方法提供扩展类引用
  • @ParameterizedTest : 表示方法是参数化测试
  • @RepeatedTest : 表示方法可重复执行
  • @Nested : 内嵌单元测试
  • @TestFactory : 动态的创建单元测试

1. 参数化测试 @ParameterizedTest

指定入参,将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。

需要引入的包

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
  • @ValueSource : 为参数化测试指定入参来源,支持八大基础类以及 String 类型,Class 类型
  • @NullSource : 表示为参数化测试提供一个 null 的入参
  • @EnumSource : 表示为参数化测试提供一个枚举入参
  • @CsvFileSource :表示读取指定 CSV 文件内容作为参数化测试入参
  • @MethodSource :表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 字符传参
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试1")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}

// 文件传参
@ParameterizedTest
@CsvFileSource(resources = "/test.csv") //指定csv文件位置
@DisplayName("参数化测试-csv文件")
public void parameterizedTest2(String name, Integer age) {
System.out.println("name:" + name + ",age:" + age);
Assertions.assertNotNull(name);
Assertions.assertNotNull(age);
}

// 方法传参
@ParameterizedTest
@MethodSource("method") //指定方法名
@DisplayName("方法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}

static Stream<String> method() {
return Stream.of("apple", "banana");
}

2. 重复测试 @RepeatedTest

允许某个单元测试执行多次,多次运行单元测试可以更加保证测试的准确性,防止一些随机性。

1
2
3
4
5
@RepeatedTest(10) //表示重复执行10次
@DisplayName("重复测试")
public void testRepeated() {
Assertions.assertTrue(1 == 1);
}

3. 内嵌单元测试 @Nested

平时我们写单元测试时一般都是一个类对应一个单元测试类。不过有些互相之间有业务关系的类,他们的单元测试完全是可以写在一起,使用内嵌的方式表示,减少测试类的数量防止类爆炸。@Nested 注解,能够以静态内部成员类的形式对测试用例类进行逻辑分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class NestedTestDemo {

@Test
@DisplayName("Nested")
void isInstantiatedWithNew() {
System.out.println("最一层--内嵌单元测试");
}

@Nested
@DisplayName("Nested2")
class Nested2 {

@BeforeEach
void Nested2_init() {
System.out.println("Nested2_init");
}

@Test
void Nested2_test() {
System.out.println("第二层-内嵌单元测试");
}


@Nested
@DisplayName("Nested3")
class Nested3 {

@BeforeEach
void Nested3_init() {
System.out.println("Nested3_init");
}

@Test
void Nested3_test() {
System.out.println("第三层-内嵌单元测试");
}
}
}

}

4. 动态的创建单元测试 @TestFactory

@TestFactory 修饰的方法本身并不是单元测试,其会在运行时生成单元测试,通过返回 DynamicTest 的迭代器或流即可获取。

1
2
3
4
5
6
7
8
@TestFactory
@DisplayName("动态测试")
Iterator<DynamicTest> dynamicTests() {
return Arrays.asList(
dynamicTest("第一个动态测试", () -> assertTrue(true)),
dynamicTest("第二个动态测试", () -> assertEquals(4, 2 * 2))
).iterator();
}

4. 断言

org.junit.jupiter.api.Assertions,支持 lambda 表达式

1. 异常断言

1
2
3
4
5
6
7
@Test
@DisplayName("异常测试")
public void exceptionTest() {
ArithmeticException exception = Assertions.assertThrows(
// 扔出断言异常
ArithmeticException.class, () -> System.out.println(1 % 0));
}

2. 超时断言

1
2
3
4
5
6
@Test
@DisplayName("超时测试")
public void timeoutTest() {
// 如果测试方法时间超过 1s 将会异常
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}