浅谈java单元测试框架junit4/5

article/2025/10/2 13:56:25

0 前言

junit是一个开源的Java语言的单元测试框架。目前junit主要有版本junit3,junit4和junit5。因在junit3中,是通过对测试类和测试方法的命名来确定是否是测试,且所有的测试类必须继承junit的测试基类TestCase,所以本文不再讨论junit3,只讨论使用比较多的junit4和junit5。

0.1 特性

  • 提供注解标识测试方法;
  • 提供断言测试预期结果;
  • 提供测试套件,组织测试用例和其他测试套件;
  • 其他参见junit官网:JUnit4/JUnit5/Introduction - Junit 5官方文档中文版

1 基本用法

1.1 常用注解介绍

(1)@Test

使用@Test注解测试方法。但测试方法必须是 public void。方法名一般为testXXX,通常需要见名知起义。

(2)@BeforeClass和@AfterClass

  • @BeforeClass:会在测试类测试方法执行之前执行一次;
  • @AfterClass:会在测试内测试方法均执行完成后执行一次;

注意,@BeforeClass和@AfterClass注解的方法必须是static方法。

(3)@Before和@After

  • @Before:会在每个测试方法执行之前执行一次;
  • @After:会在每个测试方法执行之后执行一次;

(4)@Parameters

使用@Parameters注解数据源方法。

(5)@Ignore

使用@Ignore忽略测试方法,被该注解标识的测试方法会被忽略不执行。

1.2 测试样例

本文代码详情请见:https://github.com/X-NaN/studyjunit

public class JunitAnnotationTest {/*** @BeforeClass 注解的必须是static方法*/@BeforeClasspublic static void beforeClass() {System.out.println("@BeforeClass: 在该测试类内所有方法之前执行,只执行一次");}@Beforepublic void beforeMethod() {System.out.println("@Before: 在每个测试方法之前执行一次");}@Testpublic void testCaseA() {System.out.println("@Test: 标识测试方法testCaseA");}@Testpublic void testCaseB() {System.out.println("@Test: 标识测试方法testCaseB");}/*** 异常测试*/@Test(expected = ArithmeticException.class)public void testCaseException() {System.out.println("@Test: 标识测试方法testCaseException, 异常测试");System.out.println(1 / 0);}/*** 超时测试** @throws InterruptedException*/@Test(timeout = 1000)public void testCaseTimeOut() throws InterruptedException {System.out.println("@Test: 标识测试方法testCaseTimeOut,超时");// 若方法的超时时间超过timeout,则用例失败,否则成功Thread.sleep(1000);}@Ignorepublic void testCaseC() {System.out.println("@Ignore: 标识测试方法被忽略,不执行");}@Afterpublic void afterMethod() {System.out.println("@After: 在每个测试方法之后执行一次");}/*** @AfterClass 注解的必须是static方法*/@AfterClasspublic static void afterClass() {System.out.println("@AfterClass: 在该测试类中所有测试方法执行完之后执行,只执行一次");}}

测试类运行结果:

2 参数化测试

参数化测试指的是通过传入不同的测试数据,从而可以多次运行同一个用例。junit使用@Parameters注解数据源方法。编写参数化测试的步骤是

  • 使用@Parameters注解测试数据源方法;
  • 声明实例变量用于接收测试数据,并使用@Parameter注解。若测试方法需要两个入参,则需要声明两个实例变量分别接收。除了通过注解@Parameter接收测试数据,也可以通过定义构造函数用于给实例变量赋值实现测试数据绑定到实例变量。
  • 定义测试方法,使用实例变量。
  • 测试类执行器使用Parameterized,即在测试类增加注解@RunWith(Parameterized.class)。
@RunWith(Parameterized.class)
public class ParameterizedTest {/*** 必须是public且用@Parameterized.Parameter注解,括号内的为某行的第几个测试数据*/@Parameterized.Parameter(1)public Integer a;@Parameterized.Parameter(0)public Integer b;private Calculate calculate;/*** 数据源,必须是public static,且方法必须返回测试数据集合** @return*/@Parameterized.Parameterspublic static Collection data() {return Arrays.asList(new Object[][]{{0, 0},{1, 1},{2, 3},{3, 7},{10, 5},});}@Beforepublic void beforeMethod() {calculate = new Calculate();}@Testpublic void testAdd() {System.out.println(a + "+" + b + "=" + calculate.add(a, b));}}

2.1 识别测试用例

从上面参数化测试用例可以看出,参数化用例名默认为 :caseName[index]的形式。如果想要准确地识别生成的用例对应哪条数据比较困难。实际@Parameters有个name属性,可以指定参数,如下所示。

  • {index}: 代表当前参数的索引;
  • {0}, {1}, …: 代表第一个参数,第二个参数等;
/**
* 数据源,必须是public static,且方法必须返回测试数据集合
* name指定用例名称,默认使用测试数据索引序号
*
* @return
*/
@Parameterized.Parameters(name = "{index}:a={0},b={1}")public static Collection data() {return Arrays.asList(new Object[][]{{0, 0},{1, 1},{2, 3},{3, 7},{10, 5},});
}

3 分组测试

3.1 测试suite

随着测试类的不断增加,如果组织和运行一批测试类成为关键。junit提供了测试套件功能,通过将一组相关的测试类组织在一个测试套件内,使其可以一次执行。测试套件执行,使用单独的执行器Suite.class。

  • @RunWith(Suite.class)注解的类为测试套件的入口类。
  • @Suite.SuiteClasses放入相关测试类
/*** 套件类,以suite执行用例** @author xingnana* @create 2022/9/1*/
@RunWith(Suite.class)
@Suite.SuiteClasses({CalculateTest.class, CalculateAnotherTest.class})
public class JunitSuites {
}public class CalculateTest {@Testpublic void testAdd() {Calculate calculate = new Calculate();Assert.assertEquals(6, calculate.add(2, 3));}}public class CalculateAnotherTest {@Testpublic void testSubtract() {Calculate calculate = new Calculate();Assert.assertEquals(2, calculate.subtract(6, 4));}
}

3.2 分组测试

测试套件Suite是测试类级别分组,粒度比较粗,那如何实现用例级别的分组呢?junit提供了@Cate

/*** 分组测试** @author: xingnana* @date: 2022/9/9*/
@RunWith(Categories.class)
@Categories.IncludeCategory({FastTests.class})
@Suite.SuiteClasses({ATest.class, BTest.class})
public class GroupTestSuite {
}/*** 测试类别Fast** @author: xingnana* @date: 2022/9/9*/
public interface FastTests {
}/*** 测试类别Slow** @author: xingnana* @date: 2022/9/9*/
public interface SlowTests {
}public class ATest {/*** 给测试方法分类*/@Category(FastTests.class)@Testpublic void testA1(){Assert.assertEquals("aa","bb");}@Testpublic void TestA2(){System.out.printf("打印");}
}@Category({SlowTests.class, FastTests.class})
public class BTest {@Testpublic void testB1() {Assert.assertEquals("aa","bb");}@Testpublic void TestB2() {Assert.assertEquals("aa","aa");}
}

4 junit5和junit4的对比

4.1 junit5介绍

junit5是Junit框架的一个大的更新,与以前版本的 JUnit 不同,JUnit 5由来自三个不同子项目的几个不同模块组成。

JUnit Platform:是JVM 上启动测试框架的基础。它定义了 TestEngine API,用于开发在平台上运行的测试框架。此外,该平台还提供了一个 Console Launcher,用于从命令行启动平台,以及 JUnit Platform Suite Engine,用于在平台上使用一个或多个测试引擎运行自定义测试套件。

JUnit Jupiter:是用于编写 JUnit5中的测试和扩展的编程模型和扩展模型的组合,是Junit5的核心。该子项目提供了一个 TestEngine,用于在平台上运行基于 Jupiter 的测试。

JUnit Vintage:提供了一个 TestEngine,用于在平台上运行基于 JUnit3和 JUnit4的测试。

(该图取自该升级你的JUnit版本了——JUnit5基本介绍 - 知乎)

juni5与junit4的测试基本相同,但又有些区别,本文后半部分将对junit5和junit4的不同做一个介绍。

4.2 注解的区别

junit4

junit5

说明

@Test

@Test

注解测试用例

@BeforeClass

@AfterClass

@BeforeAll

@AfterAll

在测试类内所有方法之前/之后执行一次

@Before

@After

@BeforeEach

@AfterEach

在测试用例执行之前/之后执行一次

@Ignore

@Disabled

注解测试用例忽略不执行

@Category

@Tag

测试用例分类

4.2.1 测试样例

public class Junit5AnnotationTest {/*** @BeforeAll 注解的必须是static方法*/@BeforeAllpublic static void beforeAll() {System.out.println("@BeforeAll: 在该测试类内所有方法之前执行,只执行一次");}@BeforeEachpublic void beforeEachMethod() {System.out.println("@BeforeEach: 在每个测试方法之前执行一次");}@Testpublic void testCaseA() {System.out.println("@Test: 标识测试方法testCaseA");}@Testpublic void testCaseB() {System.out.println("@Test: 标识测试方法testCaseB");}/*** 异常测试*/@Testpublic void testCaseException() {System.out.println("@Test: 标识测试方法testCaseException, 异常测试");Assertions.assertThrows(ArithmeticException.class, () -> {System.out.println(1 / 0);});}/*** 超时测试*/@Testpublic void testCaseTimeOut_A() {System.out.println("testCaseTimeOut,超时");// 若方法的超时时间超过timeout,则用例失败,否则成功Assertions.assertTimeout(Duration.ofMillis(2000), () -> Thread.sleep(3000));}/*** 超时测试* https://stackoverflow.com/questions/68483928/junit-5-test-not-failing-despite-timeout* https://github.com/junit-team/junit5/issues/2087*/@Test@Timeout(value = 2000, unit = TimeUnit.MILLISECONDS)public void testCaseTimeOut_B() {System.out.println("@Timeout超时");while (true) {try {Thread.currentThread().sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}/*** @Disabled注解忽略不执行的用例*/@Disabledpublic void testCaseC() {System.out.println("@Disabled: 标识测试方法被忽略,不执行");}@Testpublic void testCaseD() {Assertions.assertEquals(5, 4, "value not equal");}@AfterEachpublic void afterEachMethod() {System.out.println("@AfterEach: 在每个测试方法之后执行一次");}/*** @AfterAll 注解的必须是static方法*/@AfterAllpublic static void afterAll() {System.out.println("@AfterAll: 在该测试类中所有测试方法执行完之后执行,只执行一次");}
}

4.2.2 超时测试

public class TimeoutDemo {@BeforeEach@Timeout(5)void setUp() {// fails if execution time exceeds 5 seconds}@Test@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)void failsIfExecutionTimeExceeds500Milliseconds() {// fails if execution time exceeds 500 millisecondstry {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}//    @Test
//    @Timeout(value = 500, unit = TimeUnit.MILLISECONDS, threadMode = ThreadMode.SEPARATE_THREAD)
//    void failsIfExecutionTimeExceeds500MillisecondsInSeparateThread() {
//        // fails if execution time exceeds 500 milliseconds, the test code is executed in a separate thread
//    }
}

遗留问题:

/**** Junit5AnnotationTest中的超时测试用例 @Timeout不生效????* https://stackoverflow.com/questions/68483928/junit-5-test-not-failing-despite-timeout* https://github.com/junit-team/junit5/issues/2087*/@Test@Timeout(value = 2000, unit = TimeUnit.MILLISECONDS)public void testCaseTimeOut_B() {System.out.println("@Timeout超时");while (true) {try {Thread.currentThread().sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}/*** TimeoutDemo类中的超时用例 @Timeout生效* https://stackoverflow.com/questions/68483928/junit-5-test-not-failing-despite-timeout* https://github.com/junit-team/junit5/issues/2087*/@Test@Timeout(value = 500, unit = TimeUnit.MILLISECONDS)void failsIfExecutionTimeExceeds500Milliseconds() {// fails if execution time exceeds 500 millisecondstry {TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}

5 如何升级到junit5

由于有些存量用例是使用junit4或3版本编写的。JUnit Vintage可以支持在升级到junit5,同时不修改原有用例的情况下运行原有的用例

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version><scope>test</scope>
</dependency>
<dependency><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId><version>5.8.2</version><scope>test</scope>
</dependency>

http://chatgpt.dhexx.cn/article/HZ6kXKti.shtml

相关文章

IDEA中使用JUnit4(单元测试框架)超详细!

IDEA中使用JUnit4教程 超详细&#xff01;(单元测试框架) 导语&#xff1a;自动化测试的必经之路–Selenium 作者&#xff1a;变优秀的小白 Github&#xff1a;YX-XiaoBai QQ交流群(new): 811792998 爱好&#xff1a;Americano More Ice ! 话不多说&#xff0c;实战为主&…

Junit 4 的使用

一、什么是 Junit 我们来百度一波&#xff0c;什么是 Junit 可以看到哈&#xff0c;Junit 是一个 Java 语言的单元测试框架&#xff0c;这个东西是程序员自测所需要的一个东西&#xff0c;这个测试也被称为白盒测试。&#xff08;下面会去说什么是白盒测试&#xff09; 我们之…

JUnit4

1.JUnit4全面引入Annotation来执行我们编写的测试 2.JUnit4并不要求测试类继承TestCase父类 3.在一个测试类中&#xff0c;所有被Test注解所修饰的public,void方法都是test case,可以被JUnit所执行。 4.虽然JUnit4并不要求测试方法名以test开头&#xff0c;但我们最好还是按照 …

Junit 4详解

Java单元机测试框架 --- Junit4 1、什么是Junit4 JUnit4是一个易学易用的Java单元测试框架,一般我们在写完一段代码或一个方法的时候,都要测试一下这段代码和这个方法的逻辑是不是正确,输入一定的数据,返回的数据是不是我们想要的结果,即我们在写单个业务代码针对结果进行…

IOS UIBUtton

Type 第二个是Customer 常用 按钮的阴影效果只能左右 这是区别于标签的地方 阴影设置没有负值 按钮的代码使用 按钮点击方法 代码设置 传参

UIButton设置图片位置

设置小图片image的位置 image默认图片保持原大小可以通过设置contentVerticalAlignment和contentHorizontalAlignment&#xff0c;修改位置&#xff0c;甚至填充满按钮 // 修改图片位置 图2的效果[button setImage:image forState:UIControlStateNormal];button.contentVerti…

UIButton基础总结

1、UIButton简介 UIButton继承自UIControl。 2、UIButton的四种状态 UIButton的四种状态分别为Normal、Highlighted、Disabled和Selected。 **&#xff08;1&#xff09;Normal&#xff1a;**按钮的普通状态&#xff0c;即为按钮的初始状态 **&#xff08;2&#xff09;Highlig…

[Swift]代码触发UIButton的点击事件

使用极光的手机号码一键登录功能&#xff0c;要求点击按钮后先弹出协议同意&#xff0c;同意协议后自动改变底部协议状态再自动代码触发登录按钮的点击事件。 OC: [but sendActionsForControlEvents:UIControlEventTouchUpInside];Swift: but.sendActions(for: .touchUpInsid…

iOS UIButton控件

UIButton是UIControl的子类&#xff0c;实现了按钮功能&#xff0c;交互事件和控件状态可查看iOS UIControl控件。 1. 初始化 通过指定按钮类型来创建UIButton对象 (instancetype)buttonWithType:(UIButtonType)buttonType;UIButtonType是一个枚举类型 值说明UIButtonTypeCu…

UIButton基础

一、UIButton基础 与UILabel相同&#xff0c;UIButton对象也需要在ViewController中写一个创建函数来建立 UIButton对象的建立如下&#xff1a; //创建普通按钮函数 - (void) createUIRectButton {//创建一个btn对象&#xff0c;更具类型来创建btn//圆角类型btn:UIButtonType…

UIButton 基础

创建一个button 注意button只能通过类方法创建&#xff0c;不能使用alloc 该段代码添加在函数- (void)viewDidLoad 中 //通过类方法创建一个UIbuttonUIButton* btn [UIButton buttonWithType:UIButtonTypeRoundedRect] ;//设置按钮的位置btn.frame CGRectMake(100, 100, 100…

UIButton基础知识和自定义详解

UIButton是我们经常用的UI控件&#xff0c;继承UIControl。这里将对UIButton的基本使用方法和自定义UIButton进行详细介绍。 一、UIBUtton基本知识介绍 对于我们学习一个新的控件、无外乎两种方法。第一种是在xcode中的.m文件查看该控件的属性和相关方法&#xff0c;第二种直…

UI基本控件(二):UIButton

UIButton——按钮 作用&#xff1a;用户交互的主要控件&#xff0c;有六种类型&#xff0c;其中自定义类型使用最为普遍 属性&#xff1a; title属性&#xff1a;是按钮的文字 titleColor属性&#xff1a;是按钮的颜色 image属性&#xff1a;是按钮显示的图像 提示&#…

oracle 用impdp 导入dmp文件

百度整理如下 /*分为7步 */ /*第1步&#xff1a;创建临时表空间(注意&#xff1a;D:\Project\OracleTableSpace\FHADMIN\ 手动创建路径) */ create temporary tablespace C##FHADMIN_TEMP tempfile D:\Project\OracleTableSpace\FHADMIN\C##FHADMIN_TEMP.dbf size 50m a…

★Oracle imp/impdp 导入dmp文件到数据库

使用EXPDP和IMPDP时应该注意的事项&#xff1a; EXP和IMP是客户端工具程序&#xff0c;它们既可以在客户端使用&#xff0c;也可以在服务端使用。 EXPDP和IMPDP是服务端的工具程序&#xff0c;他们只能在ORACLE服务端使用&#xff0c;不能在客户端使用。 IMP只适用于EX…

Oracle:使用Impdp导入dmp文件的详细过程

完全转载自&#xff1a;https://www.cnblogs.com/afei1013/p/13123784.html 这一天为了导入这个Oracle的dmp文件&#xff0c;简直就是血泪史&#xff0c;因本人对Oracle并不是很会&#xff0c;随意踩了很多小白会踩的坑&#xff0c;因此特意记录一下过程&#xff0c;防备下次的…

Oracle expdp/impdp工具使用

Oracle数据泵 一、数据泵的作用&#xff1a; 1.实现逻辑备份和逻辑恢复 2.在数据库用户之间移动对象 3.在数据库之间移动对象 4.实现表空间转移 二 、数据泵的特点与传统导出导入的区别 1.EXP和IMP是客户段工具程序&#xff0c; EXPDP和IMPDP是服务端的工具程序 2.EXP和IMP效率…

impdp导入dmp文件

impdp命令在cmd下直接用&#xff0c;不必登录oracle。只能导入expdp导出的dmp文件。 expdp导出的时候&#xff0c;需要创建 DIRECTORY 导出什么表空间&#xff0c;导入也要什么表空间。 导出什么用户&#xff0c;导入也要什么用户。 如果没有要新建。 从杭州服务器expdp导出…

解决:impdp导入.dmp文件

首先创建一个新用户&#xff0c;保证和导出dmp文件时的用户名&#xff0c;密码以及对应的表空间名相同。 不多说&#xff0c;直接上图 &#xff08;基本只用改下面备注中文部分&#xff0c;根据需求对应替换就行&#xff09; 1.创建用户 create user 用户 identified by 口…

配置NAT实现外网主机访问内网服务器(Cisco)

假设你是某公司的网络管理员&#xff0c;公司只向ISP申请了一个公网IP&#xff0c;现公司的网站在内网&#xff0c;要求在互联网也可以访问公司网站。 192.168.1.2是Web服务器的IP地址&#xff08;内网地址&#xff09;。通过分析可知&#xff0c;需要将内网服务器IP转换成外网…