单元测试之_Junit5
前言
Github:https://github.com/HealerJean
1、junit5
介绍
1.1、JUnit5
构成
JUnit5 由三个不同子项目的几个不同模块组成。
JUnit 5
=JUnit Platform
+JUnit Jupiter
+JUnit Vintage
1)
JUnit Platform
: 是在JVM
上启动测试框架的基础,不仅支持Junit
自制的测试引擎,其他测试引擎也都可以接入。2)
JUnit Jupiter
: 提供了JUnit5
的新的编程模型,是JUnit5
新特性的核心。内部包含了一个测试引擎,用于在Junit Platform
上运行。3)
JUnit Vintage
: 由于JUint
已经发展多年,为了照顾老的项目,其提供了兼容JUnit4.x
,Junit3.x
的测试引擎。
1.2、Pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
2、Junit5
基础方法
2.1、@BeforeEach
、@AfterEach
、@BeforeAll
、@AfterAll
注解 | 功能 |
---|---|
@BeforeEach | 表示在每个单元测试之前执行 |
@AfterEach | 表示在每个单元测试之后执行 |
@BeforeAll | 表示在所有单元测试之前执行 |
@AfterAll | 表示在所有单元测试之后执行 |
@Slf4j
@DisplayName("junit5功能测试")
@SpringBootTest
public class Junit5BaseTest {
@DisplayName("测试displayname注解")
@Test
void testDisplayName() {
System.out.println(1);
}
/**
* 所有测试方法运行前运行
*/
@BeforeAll
public static void beforeAll() {
System.out.println("Run before all test methods run");
}
/**
* 每个测试方法运行前运行
*/
@BeforeEach
public void beforeEach() {
System.out.println("Run before each test method runs");
}
/**
* 每个测试方法运行完毕后运行
*/
@AfterEach
public void afterEach() {
System.out.println("Run after each test method finishes running");
}
/**
* 在所有测试方法运行完毕后运行
*/
@AfterAll
public static void afterAll() {
System.out.println("Run after all test methods have finished running");
}
}
2.2、断言
2.2.1、简单断言 Assertions.assert……
方法 | 说明 |
---|---|
Assertions.assertEquals |
判断两个对象或两个原始类型是否相等 |
Assertions.assertNotEquals |
判断两个对象或两个原始类型是否不相等 |
Assertions.assertSame |
判断两个对象引用是否指向同一个对象 |
Assertions.assertNotSame |
判断两个对象引用是否指向不同的对象 |
Assertions.assertTrue |
判断给定的布尔值是否为 true |
Assertions.assertFalse |
判断给定的布尔值是否为 false |
Assertions.assertNull |
判断给定的对象引用是否为 null |
Assertions.assertNotNull |
判断给定的对象引用是否不为 null |
Assertions.assertTrue |
判断条件是否满足 |
2.2.2、数组断言 assertArrayEquals
通过
assertArrayEquals
方法来判断两个对象或原始类型的数组是否相等
@Test
@DisplayName("array assertion")
public void array() {
assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}
2.2.3、组合断言 assertAll
assertAl
l 方法接受多个org.junit.jupiter.api.Executable
函数式接口的实例作为要验证的断言1、可以通过 lambda 表达式很容易的提供这些断言
2、前边断言失败,后续代码不会执行
3、组合断言,当组合中的所有断言都通过才是true,否则是false
@Test
@DisplayName("assert all")
public void all() {
assertAll("组合断言失败",
() -> Assertions.assertEquals(2, 1 + 1),
() -> Assertions.assertTrue(false)
);
}
2.2.4、异常断言 Assertions.assertThrows()
JUnit5
提供了 **Assertions.assertThrows()
** ,配合函数式编程就可以进行使用。
@Test
@DisplayName("异常断言")
public void exceptionTest() {
Assertions.assertThrows(
ArithmeticException.class, () -> {
int i = 1 % 0;
});
}
2.2.5、超时断言 Assertions.assertTimeout()
` Junit5
提供了 **
Assertions.assertTimeout()`** 为测试方法设置了超时时间
@Test
@DisplayName("超时测试(如果测试方法时间超过1s将会异常)")
public void timeoutTest() {
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
2.2.6、快速失败
1、当满足某一条件,需要退出测试程序时,可以使用快速失败方法
2、当调用快速失败方法时,程序会退出,同时输出失败提示
@Test
@DisplayName("fail")
public void shouldFail() {
fail("This should fail");
}
2.3、前置条件
JUnit 5
中的前置条件(又叫假设)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。注意:前置条件与断言最大的区别就是前置条件不满足的情况下,跳过该方法继续执行别的方法,而不满足断言的话后面的代码都将不会执行
⬤ 当不满足前置条件时,后续输出代码未执行
⬤ 当不满足前置条件时,程序未报错退出,而是终止执行
@DisplayName("测试前置条件")
@Test
void testAssumptions() {
Assumptions.assumeTrue(false, "结果不足true");
log.info("[Junit5Test#testAssumptions] 结果不足true");
}
2.4、嵌套测试
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试
1、外层测试方法的执行,不会驱动内层方法:当内层方法中有
before
或after
等注解时,调用外层测试方法,并不会驱动其提前或最后执行2、内层方法的执行,可以驱动外层方法:当外层方法定义了某一数据结果时,内层测试方法可以直接调用该结构
@Test
@DisplayName("超时测试(如果测试方法时间超过1s将会异常)")
public void timeoutTest() {
Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}
@Nested
@DisplayName("when new")
class InnerTest {
@BeforeEach
void innerBeforeEach() {
log.info("[InnerTest#innerBeforeEach] innerBeforeEach");
}
@Test
@DisplayName("is empty")
void isEmpty() {
timeoutTest();
assertTrue(true, "参数不为true");
}
}
2.6、参数化测试
注解 | 功能 |
---|---|
@ValueSource |
为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型 |
@NullSource | 表示为参数化测试提供一个null的入参 |
@EnumSource | 表示为参数化测试提供一个枚举入参 |
@CsvFileSource | 表示读取指定CSV文件内容作为参数化测试入参 |
@CsvFileSource | 表示读取指定CSV文件内容作为参数化测试入参 |
@MethodSource |
表示读取指定方法的返回值作为参数化测试入参(方法的返回值为流) |
2.6.1、@ValueSource
入参为基础类型
@ParameterizedTest
@DisplayName("参数化测试")
@ValueSource(ints = {1, 2, 3, 4, 5})
void testParamterized(int i) {
System.out.println(i);java
}
2.6.2、@MethodSource
入参为方法返回值类型
@ParameterizedTest
@DisplayName("参数化方法测试")
@MethodSource("stringProvider")
void testParamterized2(String s) {
System.out.println(s);
}
static Stream<String> stringProvider() {
return Stream.of("apple", "banana");
}
3、单元测试覆盖率
1)pom.xml
<!-- 生成单元测试数据插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<skip>false</skip>
<testFailureIgnore>true</testFailureIgnore>
<includes>
<include>*/*Test.java</include>
</includes>
</configuration>
</plugin>
<!-- 生成JaCoCo覆盖率数据插件 -->
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
2)生成报告
做好上述报告后,直接执行
mvn test
就可以生成单测覆盖率报告了。如果没有什么异常的话,程序会生成单测覆盖率报告文件,地址为:target/site/jacoco/index.html
。
元素 | |
---|---|
Element |
|
Missed Instructions |
代码指令 统计图 |
Missed Instructions Cov |
代码指令 覆盖率 |
Missed Branches |
分支 覆盖 统计图 |
Missed Branches Cov |
分支 覆盖率 |
Missed |
圈复杂度 (循环)未覆盖数 |
Cxty |
圈复杂度 (循环) 总数 |
Missed |
行 未覆盖数 |
Lines |
行总数 |
Missed |
方法 未覆盖数 |
Methods |
方法总数 |
Missed |
类 未覆盖数 |
Classes |
类总数 |