JUnit 5 单元测试教程

news2025/8/3 23:47:55

点赞再看,动力无限。 微信搜「 程序猿阿朗 」。

本文 Github.com/niumoo/JavaNotes 和 未读代码博客 已经收录,有很多知识点和系列文章。

在这里插入图片描述

在软件开发过程中,我们通常都需要测试自己的代码运行是否正常,可能对一个函数进行简单测试,也可能是多个功能的组合测试。不管使用哪种方式,都是为了更好的测试我们的代码是否存在逻辑缺陷。测试对于软件开发是非常必要的。

JUnit 5 介绍

在 Java 中比较有名的测试工具是 JUnit ,通常我们使用 JUnit 可以对一个逻辑单元进行测试,因此也叫单元测试。多个单元测试组合测试,可以确保我们的程序符合预期。JUnit 单元测试可以在开发阶段发现问题,让我们可以提前修复代码,因此十分重要。

JUnit 5 和 JUnit

JUnit 是一个 Java 语言的开源测试框架,使用 JUnit 让我们使用注解就可以进行单元测试,很是方便。

JUnit 5 是 JUnit 的升级版本,JUnit 5 使用了 Java 8 及更高版本的 Java 语言特性,如函数编程,流式编码等,因此更加强大。JUnit 5 进行单元测试的可读性更强,编写更加容易,且可以轻松扩展。

JUnit 5 基本组件

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BAiVK3Sz-1668733594320)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/84d9e4521a6c47538d747c300b9420ad~tplv-k3u1fbpfcp-zoom-1.image)]

JUnit Platform

JUnit Platform 是 JUnit 的基础框架,使用 JUnit Platform 才能在 JVM 启动测试,JUnit Platform 还定义了 TestEngine 测试引擎,是JUnit 测试的基础。

JUnit Jupiter

JUnit Jupiter 提供了单元测试常见的注解以及扩展接口,想要方便的进行 JUnit 单元测试,那么 Jupiter 模块就必不可少。

JUnit Vintage

JUnit Vintage 提供了对 JUnit 3 和 JUnit 4 的测试支持。

JUnit 5 依赖

使用注解进行 JUnit 单元测试,直接引入 junit-jupiter即可。

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
  	<version>5.9.1</version>
    <scope>test</scope>
</dependency>

JUnit 5 常用注解

@Test

为一个 public void 方法添加 @Test 注释,允许我们对这个方法进行测试。

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/** 
 * @author:https://www.wdbyte.com  
 **/
class JUnitTestIsDog {

    @Test
    public void testIsDog() {
        String name = "cat";
        Assertions.assertEquals(name, "dog");
    }
}

上面的代码中使用了 Assertions.assertEquals(name, "dog") 来判断是否 name 变量是否是 dogAssertionsJUnit 提供的断言工具,后面会详细介绍。

idea 中运行可以到的错误日志,提示预期是 dog,实际是 cat

org.opentest4j.AssertionFailedError: 
Expected :cat
Actual   :dog
<Click to see difference>

如果是符合预期的,那么运行会显示正确标志。

@Test
public void testIsDog2() {
    String name = "dog";
    Assertions.assertEquals(name, "dog");
}

testIsDog2 方法测试通过。

在这里插入图片描述

@BeforeAll

使用 @BeforeAll 可以在单元测试前初始化部分信息,@BeforeAll 只能使用在静态方法上,被注解的方法会在测试开始前运行一次

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/** 
 * @author:https://www.wdbyte.com  
 **/
class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @Test
    public void testIsDog() {
        String name = "dog";
        Assertions.assertEquals(name, "dog");
        System.out.println("is dog");
    }

    @Test
    public void testIsCat() {
        String name = "cat";
        Assertions.assertEquals(name, "cat");
        System.out.println("is cat");
    }
}

这会输出:

初始化,准备测试信息
is cat
is dog

@BeforeEach

使用 @BeforeEach 注解的方法,会在每一个 @Test 注解的方法运行前运行一次。

class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @BeforeEach
    public void start(){
        System.out.println("开始测试...");
    }

    @Test
    public void testIsDog() {
        String name = "dog";
        Assertions.assertEquals(name, "dog");
        System.out.println("is dog");
    }

    @Test
    public void testIsCat() {
        String name = "cat";
        Assertions.assertEquals(name, "cat");
        System.out.println("is cat");
    }
}

这会输出:

初始化,准备测试信息
开始测试...
is cat
开始测试...
is dog

@AfterAll

@AfterAll 注解只能使用在静态方法上,被注解的方法会在所有单元测试运行完毕后运行一次。

class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @BeforeEach
    public void start(){
        System.out.println("开始测试...");
    }

    @Test
    public void testIsDog() {
       //...
    }

    @Test
    public void testIsCat() {
        //...
    }

    @AfterAll
    public static void close() {
        System.out.println("结束,准备退出测试");
    }
}

这会输出:

初始化,准备测试信息
开始测试...
is cat
开始测试...
is dog
结束,准备退出测试

@AfterEach

使用 @AfterEach 注解的方法,会在每一个 @Test 注解的方法运行结束前运行一次

class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @BeforeEach
    public void start(){
        System.out.println("开始测试...");
    }

    @Test
    public void testIsDog() { //... }

    @Test
    public void testIsCat() { //... }

    @AfterEach
    public void end(){
        System.out.println("测试完毕...");
    }

    @AfterAll
    public static void close() {
        System.out.println("结束,准备退出测试");
    }
}

这会输出:

初始化,准备测试信息
开始测试...
is cat
测试完毕...
开始测试...
is dog
测试完毕...
结束,准备退出测试

@Disabled

@Disabled 注解的方法不在参与测试,下面对 testIsDog 方法添加了 @Disabled 注解。

class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @BeforeEach
    public void start(){
        System.out.println("开始测试...");
    }

    @Disabled("由于xx原因,关闭 testIsDog 测试")
    @Test
    public void testIsDog() {
        String name = "dog";
        Assertions.assertEquals(name, "dog");
        System.out.println("is dog");
    }

    @Test
    public void testIsCat() {
        String name = "cat";
        Assertions.assertEquals(name, "cat");
        System.out.println("is cat");
    }

    @AfterEach
    public void end(){
        System.out.println("测试完毕...");
    }

    @AfterAll
    public static void close() {
        System.out.println("结束,准备退出测试");
    }
}

这会输出:

初始化,准备测试信息
开始测试...
is cat
测试完毕...

由于xx原因,关闭 testIsDog 测试
结束,准备退出测试

@DisplayName

使用 @DisplayName 注解可以自定义测试方法的显示名称,下面为两个测试方法自定义名称。

class JUnitBeforeAll {

    @BeforeAll
    public static void init() {
        System.out.println("初始化,准备测试信息");
    }

    @BeforeEach
    public void start() {
        System.out.println("开始测试...");
    }

    @DisplayName("是否是狗")
    @Disabled
    @Test
    public void testIsDog() {
        String name = "dog";
        Assertions.assertEquals(name, "dog");
        System.out.println("is dog");
    }

    @DisplayName("是否是猫")
    @Test
    public void testIsCat() {
        String name = "cat";
        Assertions.assertEquals(name, "cat");
        System.out.println("is cat");
    }

    @AfterEach
    public void end() {
        System.out.println("测试完毕...");
    }

    @AfterAll
    public static void close() {
        System.out.println("结束,准备退出测试");
    }
}

idea 中运行后,可以看到配置的中文名称。

在这里插入图片描述

@ParameterizedTest

使用注解 @ParameterizedTest 结合 @ValueSource ,可以对不用的入参进行测试。下面的示例使用 @ParameterizedTest 来开始参数化单元测试,name 属性用来定义测试名称, @ValueSource 则定义了两个测试值。

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class JUnitParam {

    //@Test
    @DisplayName("是否是狗")
    @ValueSource(strings = {"dog", "cat"})
    @ParameterizedTest(name = "开始测试入参 {0} ")
    public void testIsDog(String name) {
        Assertions.assertEquals(name, "dog");
    }
}

这会输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LvYoVAim-1668733594323)(https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c451427fdaa848518e1276b6094f9ff1~tplv-k3u1fbpfcp-zoom-1.image)]

@Order

在类上增加注解 @TestMethodOrder ,然后在方法上使用 @Order 指定顺序,数字越小优先级越搞,可以保证测试方法运行顺序。

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.condition.EnabledOnJre;

import static org.junit.jupiter.api.condition.JRE.JAVA_19;

@TestMethodOrder(OrderAnnotation.class)
public class JUnitOrder{

    @Test
    @DisplayName("测试是否是狗")
    @Order(2)
    public void testIsDog() {
        String name = "dog";
        Assertions.assertEquals(name, "dog");
        System.out.println("is dog");
    }

    @DisplayName("是否是猫")
    @Test
    @Order(1)
    public void testIsCat() {
        String name = "cat";
        Assertions.assertEquals(name, "cat");
        System.out.println("is cat");
    }
}

这会输出:

is cat
is dog

其他注解

@EnabledOnJre(JAVA_19)

只在 JRE 19 环境运行,否则运行会输出:Disabled on JRE version: xxx.

@RepeatedTest(10)

重复测试,参数 10 可以让单元测试重复运行 10 次。

JUnit 5 常用断言

在上面的例子中,已经用到了 assertEquals 来判断结果是否符合预期,assertEquals是类 org.junit.jupiter.api.Assertions 中的一个方法;除此之外,还几乎包括了所有我们日常测试想要用到的判断方法。

下面是一些演示:

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JunitAssert {

    @DisplayName("是否是狗")
    @Test
    public void testIsDog() {
        String name = "dog";
        Assertions.assertNotNull(name);
        Assertions.assertEquals(name, "dog");
        Assertions.assertNotEquals(name, "cat");
        Assertions.assertTrue("dog".equals(name));
        Assertions.assertFalse("cat".equals(name));
    }

    @DisplayName("是否是猫")
    @Test
    public void testIsCat() {
        String name = "cat";
        Assertions.assertNull(name, "name is not null");
    }

}

testIsDog 中演示了一些常用的判断方法,且都可以通过验证。在 testIsCat 方法中进行了 null 值判断,显然这里无法通过测试,会抛出自定义异常 name is not null

这会输出:

org.opentest4j.AssertionFailedError: name is not null ==> 
Expected :null
Actual   :cat
<Click to see difference>

预期是一个 null 值,实际上是一个 cat 字符串。

Maven JUnit 测试

在 Maven 中进行 JUnit 测试,可以通过命令 mvn test 开始测试,默认情况下会测试所有依赖了当前源码的 JUnit 测试用例。

准备被测 Preson类放在 src.main.java.com.wdbyte.test.junit5.

package com.wdbyte.test.junit5;

public class Person {
    public int getLuckyNumber() {
        return 7;
    }
}

编写测试类 PersonTest 放在 src.test.java.com.wdbyte.test.junit5. 这里判断获取到的幸运数字是否是 8 ,明显方法返回的是 7 ,所以这里是测试会报错。

package com.wdbyte.test.junit5;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

@DisplayName("测试 Presion")
class PersonTest {

    @DisplayName("测试幸运数字")
    @Test
    void getLuckyNumber() {
        Person person = new Person();
        Assertions.assertEquals(8, person.getLuckyNumber());
    }
}

在 pom.xml 中引入 maven junit 测试依赖插件。

<build>
     <plugins>
         <plugin>
             <artifactId>maven-surefire-plugin</artifactId>
             <version>2.22.2</version>
         </plugin>
         <plugin>
             <artifactId>maven-failsafe-plugin</artifactId>
             <version>2.22.2</version>
         </plugin>
     </plugins>
</build>

执行测试命令:mvn test

➜  junit5-jupiter-starter git:(master) ✗ mvn test
[INFO] Scanning for projects...
[INFO] ....
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.wdbyte.test.junit5.PersonTest
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.031 s <<< FAILURE! - in com.wdbyte.test.junit5.PersonTest
[ERROR] getLuckyNumber  Time elapsed: 0.026 s  <<< FAILURE!
org.opentest4j.AssertionFailedError: expected: <8> but was: <7>
	at com.wdbyte.test.junit5.PersonTest.getLuckyNumber(PersonTest.java:18)

[INFO]
[INFO] Results:
[INFO]
[ERROR] Failures:
[ERROR]   PersonTest.getLuckyNumber:18 expected: <8> but was: <7>
[INFO]
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.777 s
[INFO] Finished at: 2022-11-17T23:01:09+08:00
[INFO] ------------------------------------------------------------------------

也可以指定类进行测试:mvn -Dtest=PersonTest test

一如既往,文章中代码存放在 Github.com/niumoo/javaNotes.

<完>

文章持续更新,可以微信搜一搜「 程序猿阿朗 」或访问「程序猿阿朗博客 」第一时间阅读。本文 Github.com/niumoo/JavaNotes 已经收录,有很多知识点和系列文章,欢迎Star。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/15846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

传奇开服教程——legend/blue引擎替换和登陆器生成教程

1. 下载好legend/blue引擎的服务端解压到D盘 2. 下载legend/blue引擎和登陆器配置器 3. 解压legend/blue引擎和配置器到任意目录&#xff0c;运行对应引擎中的 开始更新程序.bat 就完成引擎替换&#xff0c;接着往下看 4. 打开登陆器配置器(Blue-LEG)中的 登陆器配置器-…

区块链软件开发中的虚拟机(virtual machine)

一、什么是虚拟机 虚拟机&#xff08;英语&#xff1a;virtual machine&#xff09;&#xff0c;在计算机科学中的体系结构里&#xff0c;是指一种特殊的软件&#xff0c;可以在计算机平台和终端用户之间创建一种环境&#xff0c;而终端用户则是基于虚拟机这个软件所创建的环境…

ORACLE 19C pdb修改的参数保存在哪个数据字典中?

PDB关闭后&#xff0c;保存在&#xff1a; pdb_spfile$中。 下面举例&#xff1a; 在PDB1中修改 ddl_lock_timeout10 SQL> alter session set containerpdb1; Session altered. SQL> show parameter ddl_ NAME TYPE VALUE ------------------------------------ --------…

路由进阶:双点双向路由重发布实验配置

实验拓扑 网络拓扑及IP编址如上图所示&#xff1b;设备的互联地址为192.168.xy.0/24。其中x、y为设备编号。例如R1-R3之间互联的链路网段为192.168.13.0/24&#xff0c;并且R1的接口地址为192.168.13.1&#xff0c;R3的接口地址为192.168.13.3&#xff0c;也就是说IP地址的最后…

Mac电脑升级13系统后,git clone 代码报错,mac升级后git ssh用不了

mac系统出了新版本 13.0.1 后&#xff0c;androidstudio push, pull 代码一直报错&#xff1a; 如下&#xff1a; Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the reposito…

基于PHP+MySQL的校园餐厅展示订餐系统

校园餐厅展示系统主要是实现用户在线订餐功能,由于校园餐厅每天的销售量大,而传统的订餐一直采用手工操作,尤其是在订餐管理部分存在工作量大效率低、日常订单数据经常出错、订单查询困难、用户需求信息不能及时掌握等一系列的问题。校园餐厅展示系统的开发应用,能够实现用户的…

《围城》笔记

《围城》&#xff0c;依稀记得初中时看到较高年级的学长手里攥着去上课&#xff0c;这位学长是个文科生&#xff0c;属于比较爱看小说的类型。前些天偶尔看到围城这个熟悉的名字&#xff0c;于是看了看。 杨绛女士在文章的最后面提到了钱钟书写《围城》的背景&#xff0c;她是陪…

A Philosophy of Software Design读书笔记——分or合

软件设计中有个很重要的问题&#xff1a;两个功能&#xff0c;是分成两部分实现&#xff0c;还是合在一部分实现呢 合or分的目的是减少系统复杂度&#xff0c;但是拆分有带来一些复杂度&#xff0c;比如&#xff1a; 1、组件个数增加复杂性&#xff0c;难以追踪每个组件&…

Foxit PDF SDK for Linux (C++ Library) 8.4.1 Crack

适用于Linux平台的行业优秀的PDF技术 Foxit PDF SDK Linux版本&#xff0c;一款提供给Linux平台开发人员的强大的PDF库。它提供了简单易用的C接口和Java接口&#xff0c;允许开发人员将PDF显示、导航、创建、搜索、注释、保护、PDF文本提取、图片转换、表单数据收集和编辑功能…

JavaScript代码是怎么在浏览器里面运行起来的?

JavaScript代码是怎么在浏览器里面运行的&#xff1f;下面简单探索一下 浏览器内核 浏览器内核&#xff08;Rendering Engine&#xff09;&#xff0c;常见的叫法如&#xff1a;排版引擎、解释引擎、渲染引擎&#xff0c;现在流行称为浏览器内核。 浏览器内核说明IETridentIE…

3415: 【提高】小 X 的佛光

3415: 【提高】小 X 的佛光 时间限制: 1.000 Sec 内存限制: 128 MB 提交: 14 解决: 7 [命题人:][下载数据: 110] 提交状态报告 题目描述 题目背景】 小 X 是远近闻名的学佛&#xff0c;平日里最喜欢做的事就是蒸发学水。 【题目描述】 小 X 所在的城市 X 城是一个含有 N…

XSS-labs靶场实战(二)——第4-6关

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是XSS-labs靶场实战第4-6关。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未…

Qt ARM+Linux平台调用一个库的时候,报错“Bus error”

Qt ARMLinux平台调用一个库的时候&#xff0c;报错“Bus error” 文章目录Qt ARMLinux平台调用一个库的时候&#xff0c;报错“Bus error”摘要1 字节对齐问题2内存溢出问题关键字&#xff1a; Bus error、 Qt、 Arm、 Linux、 Debian内容背景&#xff1a; 最近项目终于切到Li…

如何实现高效地扩展5G毫米波段?

如今的高频信号标准使用的是比以往更高的频率和更宽的带宽&#xff0c;经过多年的研究和测试&#xff0c;5G无线网络正在世界各地进行部署。5G利用比以前使用的频带高得多的频段和毫米波频率&#xff0c;实现了高速、宽带宽、低时延和极高的容量。 然而&#xff0c;这些高频信号…

赋能型细分定位该不该选择?—— FB推广

赋能型细分定位的作用&#xff1a; 赋能型细分定位可以使系统覆盖到比您在细分定位选项中定义的用户更为广阔的用户群体&#xff0c;从而帮助改善广告表现。&#xff08;如果了解谷歌的话&#xff0c;可以理解为搜索网络合作伙伴&#xff09; 如果您希望我们面向更多可能助您…

Nginx源码:内存池的实现

文章目录1、数据结构2、接口函数2.1、创建内存池2.2、内存分配2.2.1、小块内存分配2.2.2、大块内存分配2.3、内存释放2.3.1、大块内存释放2.3.2、内存池释放4、参考为什么需要对内存管理&#xff1f; 避免频繁的系统调用带来的开销。减少了频繁分配和释放小块内存产生的内存碎…

LeetCode刷题(python版)——Topic72. 编辑距离

一、题设 给你两个单词 word1 和 word2&#xff0c; 请返回将 word1 转换成 word2 所使用的最少操作数 。 你可以对一个单词进行如下三种操作&#xff1a; 插入一个字符删除一个字符替换一个字符 示例 1&#xff1a; 输入&#xff1a;word1 "horse", word2 &q…

回溯算法的应用

基本思想&#xff1a; 回溯法从开始结点(根结点)出发,以深度优先方式搜索整个解空间。这个开始结点成为活结点,同时也成为当前的扩展结点。在当前扩展结点处,搜索向纵深方向移至一个新结点。这个新结点成为新的活结点,开成为当前扩展结点。如果在当前扩展结点处不能再向纵深方…

大数据技术基础实验十五:Storm实验——实时WordCountTopology

大数据技术基础实验十五&#xff1a;Storm实验——实时WordCountTopology 文章目录大数据技术基础实验十五&#xff1a;Storm实验——实时WordCountTopology一、前言二、实验目的三、实验要求四、实验原理1、Topologies2、Spouts3、Bolts五、实验步骤1、导入依赖jar包2、编写代…

ffmpeg编译so

1.第一个坑&#xff1a;/bin/bash^M: bad interpreter: No such file or directory shell脚本报错/bin/bash^M: bad interpreter: No such file or directory&#xff0c;通过查阅资料得知&#xff0c;shell脚本格式必须是unix才行&#xff0c;但我这个脚本是在windows上编写完…