【单元测试】如何使用 JUnit5 框架?

JUnit5 单元测试框架使用教程

一、Junit5 是什么?

  Junit5是一个用于在Java平台上进行单元测试的框架。JUnit 5 框架主要由三部分组成:JUnit Platform、JUnit Jupiter 和 JUnit Vintage。

  • JUnit Platform:定义了测试引擎的 API,是 JVM 上用于启动测试框架的基础服务,支持通过 IDE、构建工具、命令行等方式运行单元测试。
  • JUnit Jupiter:包含 JUnit 5 新的编程模型和扩展模型,主要用于编写和扩展测试代码。
  • JUnit Vintage:兼容运行 JUnit 3 和 JUnit4 编写的测试用例。

二、Junit5 的注解

(一)导入依赖

  导入五个依赖:

   <!--  junit-jupiter-api 里有 @BeforeAll……等注解  --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.10.0</version></dependency><!--   junit-jupiter-params 里有 @ValueSource……等注解    --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-params</artifactId><version>5.10.0</version></dependency><!--    用于运行 识别上述注解    --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.10.0</version><scope>test</scope></dependency><!-- 测试套件  --><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-suite-api</artifactId><version>1.10.0</version></dependency><!-- 运行测试套件的测试引擎--><dependency><groupId>org.junit.platform</groupId><artifactId>junit-platform-suite-engine</artifactId><version>1.10.0</version><scope>test</scope></dependency>

(二)常用的注解

如果你的IDEA在使用JUnit注解的时候发生如下情况:依赖已经导入且加载完成,但是IDEA没能识别出来,如图:

image.png

有的注解也在params包中。

image.png

(我真的不理解为啥)

有一种解决办法:

image.png

image.png

image.png

点击后,选择相应的版本,我这里是5.10.0,点了之后IDEA就能识别出来了。

现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:110685036

1.@Test
public class JUnitTest {@Testvoid test0(){System.out.println("测试用例1");}@Testvoid test1(){System.out.println("测试用例2");}@Testvoid test2(){System.out.println("测试用例3");}
}

结果:

image.png

2.@BeforeAll、@AfterAll
  • @BeforeAll:表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之前执行;
  • @AfterAll:表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之后执行;

使用@BeforeAll@AfterAll注解的方法要加上static

public class JUnitTest {@BeforeAllstatic void beforeAll(){//可以用于创建一些资源System.out.println("我是BeforeAll,我最开始执行。");}@AfterAllstatic void afterAll(){//可以用于释放资源System.out.println("我是AfterAll,我最后执行。");}@Testvoid test0(){System.out.println("测试用例1");}@Testvoid test1(){System.out.println("测试用例2");}@Testvoid test2(){System.out.println("测试用例3");}
}

image.png

3.@BeforeEach、@AfterEach
  • @BeforeEach:表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之前执行;
  • @AfterEach:表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之后执行;
public class JUnitTest {@BeforeAllstatic void beforeAll(){System.out.println("我是BeforeAll,我最开始执行。");}@AfterAllstatic void afterAll(){System.out.println("我是AfterAll,我最后执行。");}@BeforeEachvoid beforeEach(){System.out.println("我是BeforeEach,我在每个 @Test 前执行。");}@AfterEachvoid afterEach(){System.out.println("我是AfterEach,我在每个 @Test 后执行。");}@Testvoid test0(){System.out.println("测试用例1");}@Testvoid test1(){System.out.println("测试用例2");}@Testvoid test2(){System.out.println("测试用例3");}
}

结果:

image.png

4.@Disabled

  @Disabled用于禁用测试类或测试方法,添加该注解的方法不会被测试。

public class JUnitTest {@BeforeAllstatic void beforeAll(){System.out.println("我是BeforeAll,我最开始执行。");}@AfterAllstatic void afterAll(){System.out.println("我是AfterAll,我最后执行。");}@BeforeEachvoid beforeEach(){System.out.println("我是BeforeEach,我在每个 @Test 前执行。");}@AfterEachvoid afterEach(){System.out.println("我是AfterEach,我在每个 @Test 后执行。");}@Test@Disabled //忽略测试用例1void test0(){System.out.println("测试用例1");}@Testvoid test1(){System.out.println("测试用例2");}@Testvoid test2(){System.out.println("测试用例3");}
}

结果:

image.png

(三)参数化测试

1.@ParameterizedTest + @ValueSource

  @ParameterizedTest的作用就是可以用不同的参数多次运行测试。但是必须声调用提供参数的来源(source)。

  @ValueSource它可以让你指定一个原生类型(String,int,long或double)的数组,并且只能为每次调用提供一个参数。

public class JUnitTest {@BeforeAllstatic void beforeAll(){System.out.println("我是BeforeAll,我最开始执行。");}@AfterAllstatic void afterAll(){System.out.println("我是AfterAll,我最后执行。");}@BeforeEachvoid beforeEach(){System.out.println("我是BeforeEach,我在每个 Test 前执行。");}@AfterEachvoid afterEach(){System.out.println("我是AfterEach,我在每个 Test 后执行。");}@Testvoid test0(){System.out.println("测试用例1");}@Testvoid test1(){System.out.println("测试用例2");}@Testvoid test2(){System.out.println("测试用例3");}@ParameterizedTest@ValueSource(strings = {"小明","小红","小兰"})void paramTest(String name){System.out.println(name);}
}

结果:

image.png

2.@ParameterizedTest + @CsvSource

  @CsvSource允许将参数列表表示为以逗号分隔的值(例如,字符串文字)。

public class JUnitTest { @ParameterizedTest@CsvSource({"小明, 1","小红,2","小兰,3"})void csvSource(String name,int id){System.out.println(name + ":" + id);}
}

结果:

image.png

@CsvSource使用'作为转义字符。

示例输入结果字符列表
@CsvSource({ "foo, bar" })"foo", "bar"
@CsvSource({ "foo, 'baz, qux'" })"foo", "baz, qux"
@CsvSource({ "foo, ''" })"foo", ""
@CsvSource({ "foo, " })"foo", null
3.@ParameterizedTest + @CsvFileSource

  @CsvFileSource让你使用classpath中的CSV文件。CSV文件中的每一行都会导致参数化测试的一次调用。

resources目录下创建csv文件:

test.csv:

小明, 1
小红, 2
"小明, 小红", 3
public class JUnitTest {@ParameterizedTest@CsvFileSource(resources = "/test.csv")void csvFile(String name,int id){System.out.println(name + ": " + id);}
}

结果:

image.png

@CsvSource中使用的语法相反,@CsvFileSource使用双引号"作为转义字符。通过上面的代码就可以看出来。一个空的转义值""会产生一个空字符串, 一个完全为空的值被解释为null引用。

4.@ParameterizedTest + @MethodSource

  @MethodSource允许引用一个或多个测试类的工厂方法。

public class JUnitTest {@ParameterizedTest@MethodSource("stringProvider") //指定方法void methodSource(int age,String name){System.out.println(age + ": " + name);}static Stream<Arguments> stringProvider() {return Stream.of(Arguments.arguments(12,"李四"),Arguments.arguments(18,"王五"),Arguments.arguments(20,"小红"));}
}

  @MethodSource注解表示这个方法的参数来源于一个名为stringProvider的静态方法。stringProvider方法返回一个Stream<Arguments>类型的对象,其中每个Arguments对象包含了一组用于测试的参数。

image.png

(四)测试方法的执行顺序

1.@TestMethodOrder + @Order

  在 JUnit5 中,测试方法执行的顺序是不确定的或者是根据方法首字母来排序的。

public class JUnitTest2 {@Testvoid C(){System.out.println("A");}@Testvoid B(){System.out.println("B");}@Testvoid A(){System.out.println("C");}
}

结果:

image.png

  让执行顺序为 C、B、A:

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class JUnitTest2 {int a = 0;@Test@Order(1)void C(){a++;System.out.println(a);System.out.println("C");}@Test@Order(2)void B(){a++;System.out.println(a);System.out.println("B");}@Test@Order(3)void A(){a++;System.out.println(a);System.out.println("A");}
}

  首先在类上添加@TestMethodOrder(MethodOrderer.OrderAnnotation.class),然后再为每个方法上添加@Order()注解,值越小越优先被执行。

(五)测试实例的生命周期

1.@TestInstance

  我添加一个成员变量,每次执行测试方法的时候都++一次。

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
public class JUnitTest2 {int a = 0;@Test@Order(1)void A(){a++;System.out.println("A方法:" + a);System.out.println("A");}@Test@Order(2)void B(){a++;System.out.println("B方法:" + a);System.out.println("B");}@Test@Order(3)void C(){a++;System.out.println("C方法:" + a);System.out.println("C");}
}

结果:

image.png

  为了允许隔离执行单个的测试方法,JUnit在执行每个测试方法之前会创建每个测试类的新实例。如果想改变策略,就要用@TestInstance,在类上添加@TestInstance(TestInstance.Lifecycle.PER_CLASS)

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class JUnitTest2 {int a = 0;@Test@Order(1)void A(){a++;System.out.println("A方法:" + a);System.out.println("A");}@Test@Order(2)void B(){a++;System.out.println("B方法:" + a);System.out.println("B");}@Test@Order(3)void C(){a++;System.out.println("C方法:" + a);System.out.println("C");}
}

结果:

image.png

Lifecycle.PER_CLASS表示只创建一个实例。不添加注解的时候,默认是Lifecycle.PER_METHOD

  当使用这种模式时,每个测试类将创建一个新的测试实例。因此,如果测试方法依赖于存储在实例变量中的状态,则可能需要在@BeforeEach@AfterEach方法中重置该状态(重置变量的值)。

(六)断言 Assertions

断言方法描述
assertEquals(expected, actual)检查两个值是否相等,如果不相等则抛出AssertionError
assertNotEquals(expected, actual)检查两个值是否不相等,如果相等则抛出AssertionError
assertTrue(condition)检查一个条件是否为真,如果为假则抛出AssertionError
assertFalse(condition)检查一个条件是否为假,如果为真则抛出AssertionError
assertNull(object)检查一个对象是否为null,如果不为null则抛出AssertionError
assertNotNull(object)检查一个对象是否不为null,如果为null则抛出AssertionError
assertSame(expected, actual)检查两个对象是否是同一个实例,如果不是则抛出AssertionError
assertNotSame(expected, actual)检查两个对象是否不是同一个实例,如果是则抛出AssertionError
assertArrayEquals(expected, actual)检查两个数组是否相等,如果不相等则抛出AssertionError
assertTimeout(duration, executable)检查一个可执行的代码块是否在指定的时间内完成,如果超时则抛出AssertionError
public class JUnitTest3 {@Testvoid assertEqualsDemo(){int num = 10;Assertions.assertEquals(1,num,"不符合预期");}@Testvoid assertTrueDemo(){int num = 10;Assertions.assertTrue(num > 10,"不符合预期");}@Testvoid assertTimeoutDemo(){int num = 10;Assertions.assertTimeout(Duration.ofSeconds(3), new Executable() {@Overridepublic void execute() throws Throwable {//代码块Thread.sleep(4000);}});}
}
结果:

image.png

(七)测试套件

  测试套件是一组相关的测试,可以一起运行,以便更方便地组织和管理测试。使用套件要引入两个依赖:junit-platform-suite-apijunit-platform-suite-engine,具体的在文章开头。

  套件其实很好理解,就是使几个类同时进行测试。

1.@SelectClasses
@Suite
@SelectClasses(value = {JUnitTest.class,JUnitTest2.class})
public class RunSuite {}

@Suite的作用是将一个类标记为JUnit平台上的测试套件。

@SelectClasses指定在JUnit平台上运行测试套件时要选择的类。

运行结果:

image.png

2.@SelectPackages

  可以选择类,那么也可以包。

@Suite
@SelectPackages(value = {"package1"})
//可以选择多个包:@SelectPackages(value = {"package1","package2","package3"……})
public class RunSuite {}

image.png

结果:

image.png

为什么只执行了JUnitTest这一个类?我的JUnitTest2呢?我们来看看它:

image.png

IDEA提示我们它的命名不符合规则,那这个规则是什么意思呢?

3.测试类命名规则
  • [A-Z[A-Za-z\d]*Test(s|Case)?:表示以大写字母开头,后面跟任意个字母或数字,最后以Test, Tests, TestCase结尾的字符串,例如MyTest, MyTests, MyTestCase等。
  • Test[A-Z[A-Za-z\d]*:表示以Test开头,后面跟一个大写字母,再后面跟任意个字母或数字的字符串,例如TestMyClass, TestMyMethod等。
  • IT(.*):表示以IT开头,后面跟任意个任意字符的字符串,例如ITMyClass, ITMyMethod等。
  • (.*)IT(Case)?:表示以任意个任意字符开头,后面跟IT或者ITCase的字符串,例如MyClassIT, MyMethodITCase等。

其实就是我们的类命名不规范导致框架识别不出来。改类名后:

image.png

image.png

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走!

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
 

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软考程序员考试大纲(2023)

文章目录 前言一、考试说明1.考试目标2.考试要求3&#xff0e;考试科目设置 二、考试范围考试科目1&#xff1a;计算机与软件工程基本知识1&#xff0e;计算机科学基础2&#xff0e;计算机系统基础知识3&#xff0e;系统开发和运行知识4&#xff0e;网络与信息安全基础知识5&am…

pnpm、npm、yarn 包管理工具『优劣对比』及『环境迁移』

前言 博主在开发前端网站的时候&#xff0c;发现随着开发的项目的逐渐增多&#xff0c;安装的依赖包越来越臃肿&#xff0c;依赖包的安装速度也是非常越来越慢&#xff0c;多项目开发管理也是比较麻烦。之前我就了解过 pnpm&#xff0c;但是当时担心更换包管理环境可能会出现的…

十、2023.10.4.计算机网络(one).10

文章目录 1、简述静态路由和动态路由&#xff1f;2、说说有哪些路由协议&#xff0c;都是如何更新的&#xff1f;3、简述域名解析过程&#xff0c;本机如何干预域名解析&#xff1f;4、简述 DNS 查询服务器的基本流程是什么&#xff1f;DNS 劫持是什么&#xff1f;5、简述网关的…

FFmpeg 基础模块:容器相关的 API 操作

目录 AVFormat 模块 AVFormat 前处理部分 AVFormat 读写处理部分 小结 思考 FFmpeg 目录中包含了 FFmpeg 库代码目录、构建工程目录、自测子系统目录等&#xff0c;具体内容如下&#xff1a; 现在你知道 FFmpeg 的源代码目录中都包含了哪些内容&#xff0c;在之后使用 FFm…

FFmpeg日志系统、文件与目录、操作目录

目录 FFmpeg日志系统 FFmpeg文件与目录操作 FFmpeg文件的删除与重命名 FFmpeg操作目录及list的实现 操作目录重要函数 操作目录重要结构体 FFmpeg日志系统 下面看一个简单的 demo。 #include <stdio.h> #include <libavutil/log.h>int main(int argc,char* …

多头注意力机制

1、什么是多头注意力机制 从多头注意力的结构图中&#xff0c;貌似这个所谓的多个头就是指多组线性变换&#xff0c;但是并不是&#xff0c;只使用了一组线性变换层&#xff0c;即三个变换张量对 Q、K、V 分别进行线性变换&#xff0c;这些变化不会改变原有张量的尺寸&#xf…

【Vue面试题十一】、Vue组件之间的通信方式都有哪些?

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;Vue组件之间的通信方式都…

浅谈CDN内容分发与全局负载均衡

CDN简介 CDN的全称是Content Delivery Network&#xff0c;即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c…

C语言学生成绩录入系统

一、系统概述 该系统是一个由链表创建主菜单的框架&#xff0c;旨在快速创建学生成绩录入系统的主菜单结构。其主要任务包括&#xff1a; 实现链表的创建、插入和遍历功能&#xff0c;用于存储和展示学生成绩录入系统各个模块的菜单项。 2. 提供用户友好的主菜单界面&#xf…

【回顾一下Docker的基本用法】

文章目录 回顾一下Docker的基本用法1.初识Docker1.1.什么是Docker1.1.1.应用部署的环境问题1.1.2.Docker解决依赖兼容问题1.1.3.Docker解决操作系统环境差异1.1.4.小结 1.2.Docker和虚拟机的区别1.3.Docker架构1.3.1.镜像和容器1.3.2.DockerHub1.3.3.Docker架构1.3.4.小结 1.4.…

CSS小计

1&#xff1a;设置图片随窗缩放 使用百分比 width: 100%;height: 100%; 使用vmin: 将可视区域分为100vmin width: 100vmin;height: 100vmin; 2:设置字体颜色与背景色融合 mix-blend-mode: difference 3: 设置宽度自适应 width:fit-content 4:外边距合并 当两个相领的两个容…

Git 学习笔记 | 使用码云

Git 学习笔记 | 使用码云 Git 学习笔记 | 使用码云注册登录码云&#xff0c;完善个人信息设置本机绑定SSH公钥&#xff0c;实现免密码登录创建远程仓库 Git 学习笔记 | 使用码云 注册登录码云&#xff0c;完善个人信息 网址&#xff1a;https://gitee.com/ 可以使用微信&…

RT-Thread 内存管理(学习二)

内存堆管理应用示例 这是一个内存堆的应用示例&#xff0c;这个程序会创建一个动态的线程&#xff0c;这个线程会动态申请内存并释放&#xff0c;每次申请更大的内存&#xff0c;当申请不到的时候就结束。 #include <rtthread.h>#define THREAD_PRIORITY 25 #defi…

机器学习之旅-从Python 开始

你想知道如何开始机器学习吗&#xff1f;在这篇文章中&#xff0c;我将简要概括一下使用 Python 来开始机器学习的一些步骤。Python 是一门流行的开源程序设计语言&#xff0c;也是在人工智能及其它相关科学领域中最常用的语言之一。机器学习简称 ML&#xff0c;是人工智能的一…

lv7 嵌入式开发-网络编程开发 08 TCP并发功能

目录 1 TCP 多进程并发 1.1 现象&#xff1a; 1.2 多进程并发 2 僵尸进程处理 3 TCP并发多线程 4 练习 1 TCP 多进程并发 1.1 现象&#xff1a; 之前的代码&#xff0c;先关服务端&#xff0c;再次打开会出现错误bind:Address already in use 使用setsockopt 地址快速重…

vuejs中使用axios时如何追加数据

前言 在vuejs中使用axios时&#xff0c;有时候需要追加数据,比如,移动端下拉触底加载,分页加载,滑动滚动条,等等,这时候就需要追加数据了,下面我们来演示下. 代码演示 <template><div><div><el-button type"primary" click"handleBtnGetJ…

【智能家居项目】裸机版本——设备子系统(LED Display 风扇)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 输入子系统中目前仅实现了按键输入&#xff0c;剩下的网络输入和标准输入在以后会逐步实现&am…

三十二、【进阶】hash索引结构

1、hash索引结构 &#xff08;1&#xff09;简述&#xff1a; hash索引&#xff0c;就是采用一定的hash算法&#xff0c;将键值换算成新的hash值&#xff0c;映射到对应的槽位上&#xff0c;然后存储在hash表中。 &#xff08;2&#xff09;图示&#xff1a; 2、hash索引结构…

阿里云服务器ECS详细介绍_云主机_服务器托管_弹性计算

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云服务器网分享阿…

手写Demo体验volatile可见性的作用

volatile是java的关键字&#xff0c;作用&#xff1a;①保证线程间的可见性&#xff1b;②防止指令重排。下面看一个demo&#xff0c;启动2个线程&#xff0c;一个线程读取flag变量的值&#xff0c;另外一个线程修改flag变量的值。 public class VolatileDemo {private static…