在咱们的日常开发中,代码一边编码一边自测是常有的事,做好单元测验也是一名开发应该把握的技能,不说测验搞得多么强,至少会根本的,会功用测验,会功用测验。今日来学习下 单元测验。
1.JUnit5介绍
现在主要版本是 JUnit5,所以后面的内容也都是基于 JUnit5 做相关的介绍。JUnit5 是 JUnit 单元测验结构的严重晋级,需求运转在 Java8 以上的环境。
JUnit5能够理解为是由三个不同而子项目构成:
- 1.JUnit Platform,用于JVM上启动测验结构的根底服务,供给命令行,IDE和构建工具等办法履行测验的支撑。
- 2.JUnit Jupiter,包括 JUnit 5 新的编程模型和扩展模型,主要便是用于编写测验代码和扩展代码。
- 3.JUnit Vintage,用于在JUnit 5 中兼容运转 JUnit3.x 和 JUnit4.x 的测验用例。
JUnit5目前的主要特性:
- 供给全新的断语和测验注解,支撑测验类内嵌
- 更丰厚的测验办法:支撑动态测验,重复测验,参数化测验等
- 完成了模块化,让测验履行和测验发现等不同模块解耦,削减依靠
- 供给对 Java 8 的支撑,如 Lambda 表达式,Sream API等。
2.测验环境
- Java 21
- Junit 5.10.0
3.Maven依靠
本篇学习内容涉及的依靠如下:
<!-- 根底测验 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version>
</dependency>
<!-- 带参测验 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.0</version>
</dependency>
4.典型注解
@BeforeAll
润饰 static 办法,界说整个测验类在开端前的操作,比方一些初始化的操作。
@AfterAll
润饰 static 办法,界说整个测验类在完毕时的操作,比方一些整理作业。
@BeforeEach && @AfterEach
标注在每个测验用例办法,表明办法开端前或完毕时的履行,负责该测验用例所需求的的运转环境的准备和毁掉。
@Test
表明这个测验办法是一个测验用例。
由上面的这些注解,咱们能够做出如下流程来表明单元测验的完好进程:
@DisplayName
能够加在类上,也能够加在测验办法上,相当于一个显现名。
@Disable
相当于禁用当时的测验办法,测验时就会忽略该办法。
@RepeatedTest
表明该办法需求重复运转,详细几回,能够经过参数传入。
基于上面的介绍,咱们写一个简单的类测验:
package org.example;
import org.junit.jupiter.api.*;
import java.time.Duration;
@DisplayName("我的第一个测验用例")
public class MyFirstTestCaseTest {
@BeforeAll
public static void init() {
System.out.println("初始化数据");
}
@AfterAll
public static void cleanup() {
System.out.println("整理数据");
}
@BeforeEach
public void tearUp() {
System.out.println("当时测验办法开端");
}
@AfterEach
public void tearDown() {
System.out.println("当时测验办法完毕");
}
@DisplayName("我的第一个测验")
@Test
public void testFirstTest() {
System.out.println("我的第一个测验开端");
}
@DisplayName("我的第二个测验")
@Test
public void testSecondTest() {
System.out.println("我的第二个测验开端");
}
@DisplayName("我的第三个测验")
@Disabled
@Test
public void testThirdTest() {
System.out.println("我的第三个测验开端测验");
}
@DisplayName("我的第四个测验-重复测验")
@RepeatedTest(value = 3, name = "{displayName} 第 {currentRepetition} 次")
public void repeatedTest() {
System.out.println("正在履行重复测验");
}
}
5.断语
在测验办法中,咱们常常是给定一个预期的值和测验的结果值作比较,看对应的结果怎样,由此就由相关断语:
- 断语相等,assertEqual
- 断语不等,assertNotEqual
- 多个断语,assertAll
- 断语空或非空,assertNull/assertNotNull
- 断语 true或false,assertTrue/assertFalse
- 超时断语,assertTimeout/assertTimeoutPreemptively
- 断语实例,assertInstanceOf
- 断语反常,assertThrows
因为断语类包括内容许多,每个办法实际许多重载,这里仅选择几个重点说说,其他都是相似的。
demo
@DisplayName("我的第五个测验-单个断语")
@Test
public void testSingleAssertion() {
Integer num = 1;
Assertions.assertEquals(num, 1);
}
@DisplayName("我的第六个测验-多断语")
@Test
public void testGroupAssertions() {
int[] nums = {0, 1, 2, 3, 4};
Assertions.assertAll("nums",
() -> Assertions.assertEquals(nums[0], 0),
() -> Assertions.assertEquals(nums[1], 1),
() -> Assertions.assertEquals(nums[2], 2),
() -> Assertions.assertEquals(nums[3], 3),
() -> Assertions.assertEquals(nums[4], 4)
);
}
@DisplayName("我的第七个测验-超时操作")
@Test
public void testShouldCompleteInOneSecond() {
// 无法做到时间的准确匹配
Assertions.assertTimeoutPreemptively(Duration.ofSeconds(1), () -> Thread.sleep(999));
}
@DisplayName("我的第八个测验-反常测验")
@Test
public void testAssertThrowsException() {
String str = null;
// str 作为传入参数,会报非法传参反常,所以能够正常断语到
Assertions.assertThrows(IllegalArgumentException.class, () -> {
Integer.valueOf(str);
});
}
6.带参数的测验办法
有的时分咱们需求一些带参数的测验办法,比方一次测验一个参数不行,那就来一组参数,或许有的传参需求多个参数怎么办。
这里咱们用到了 Junit5的 params 包供给功用,这也是上面咱们在依靠中添加的依靠项。
在有带参数的单元测验中主要介绍几个常用的,其他感兴趣能够看看源码。
@ParameterizedTest
此处能够用来代替 @Test 注解,相同的成效。
@ValueSource
指定咱们的传入的这一组参数,能够是:
- ints
- strings
- classes等
包括类型掩盖根本类型。
@CsvSource
规则了传入的多个入参的组合方式,默认用“,”分隔。
下面是个简单的 demo:
package org.example;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
public class ParameterUnitTest {
@ParameterizedTest
@ValueSource(ints = {2, 4, 8, 10})
public void testIsEvenNumber(int num) {
Assertions.assertEquals(0, num % 2);
}
@ParameterizedTest
@ValueSource(strings = {"Effective Java", "C Plus Plus"})
public void testPrintTitle(String title) {
System.out.println(title);
}
// 多参数
@ParameterizedTest
@CsvSource({"1,One", "2,Two"})
public void testDataFromCSV(long id, String name) {
System.out.printf("id: %d, name: %sn", id, name);
}
}
以上这些都是一些根本用法,有的时分咱们需求依靠其他类,所以就会有 mock打桩 的需求,这是后话了,把握这些根本用法能掩盖相当的开发自测了。
参阅: