Java JUnit5 使用小结

在我们的日常开发中,代码一边编码一边自测是常有的事,做好单元测试也是一名开发应该掌握的技能,不说测试搞得多么强,至少会基本的,会功能测试,会性能测试。今天来学习下 单元测试。

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

表示这个测试方法是一个测试用例。

由上面的这些注解,我们可以做出如下流程来表示单元测试的完整过程:
image.png

@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: %s\n", id, name);
}
}

以上这些都是一些基本用法,有的时候我们需要依赖其他类,所以就会有 mock打桩 的需求,这是后话了,掌握这些基本用法能覆盖相当的开发自测了。

参考:

posted on   进击的davis  阅读(182)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
历史上的今天:
2021-11-21 0090-子集II
2021-11-21 0078-子集
2021-11-21 0093-复原IP地址
2021-11-21 0131-分割回文串
2021-11-21 0216.-组合总和 III
2021-11-21 0040-组合总和II
2021-11-21 0039-组合总和

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示