点赞再看,动力无限。 微信搜「 程序猿阿朗 」。
本文 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
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
注释,允许我们对这个方法进行测试。
importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.Test;/**
* @author:https://www.wdbyte.com
**/classJUnitTestIsDog{@TestpublicvoidtestIsDog(){String name ="cat";Assertions.assertEquals(name,"dog");}}
上面的代码中使用了
Assertions.assertEquals(name, "dog")
来判断是否
name
变量是否是
dog
,
Assertions
是
JUnit
提供的断言工具,后面会详细介绍。
在
idea
中运行可以到的错误日志,提示预期是
dog
,实际是
cat
org.opentest4j.AssertionFailedError:
Expected :cat
Actual :dog
<Click to see difference>
如果是符合预期的,那么运行会显示正确标志。
@TestpublicvoidtestIsDog2(){String name ="dog";Assertions.assertEquals(name,"dog");}
testIsDog2
方法测试通过。
@BeforeAll
使用
@BeforeAll
可以在单元测试前初始化部分信息,
@BeforeAll
只能使用在静态方法上,被注解的方法会在测试开始前运行一次。
importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.BeforeAll;importorg.junit.jupiter.api.BeforeEach;importorg.junit.jupiter.api.Test;/**
* @author:https://www.wdbyte.com
**/classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@TestpublicvoidtestIsDog(){String name ="dog";Assertions.assertEquals(name,"dog");System.out.println("is dog");}@TestpublicvoidtestIsCat(){String name ="cat";Assertions.assertEquals(name,"cat");System.out.println("is cat");}}
这会输出:
初始化,准备测试信息
is cat
is dog
@BeforeEach
使用
@BeforeEach
注解的方法,会在每一个
@Test
注解的方法运行前运行一次。
classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@BeforeEachpublicvoidstart(){System.out.println("开始测试...");}@TestpublicvoidtestIsDog(){String name ="dog";Assertions.assertEquals(name,"dog");System.out.println("is dog");}@TestpublicvoidtestIsCat(){String name ="cat";Assertions.assertEquals(name,"cat");System.out.println("is cat");}}
这会输出:
初始化,准备测试信息
开始测试...
is cat
开始测试...
is dog
@AfterAll
@AfterAll
注解只能使用在静态方法上,被注解的方法会在所有单元测试运行完毕后运行一次。
classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@BeforeEachpublicvoidstart(){System.out.println("开始测试...");}@TestpublicvoidtestIsDog(){//...}@TestpublicvoidtestIsCat(){//...}@AfterAllpublicstaticvoidclose(){System.out.println("结束,准备退出测试");}}
这会输出:
初始化,准备测试信息
开始测试...
is cat
开始测试...
is dog
结束,准备退出测试
@AfterEach
使用
@AfterEach
注解的方法,会在每一个
@Test
注解的方法运行结束前运行一次。
classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@BeforeEachpublicvoidstart(){System.out.println("开始测试...");}@TestpublicvoidtestIsDog(){//... }@TestpublicvoidtestIsCat(){//... }@AfterEachpublicvoidend(){System.out.println("测试完毕...");}@AfterAllpublicstaticvoidclose(){System.out.println("结束,准备退出测试");}}
这会输出:
初始化,准备测试信息
开始测试...
is cat
测试完毕...
开始测试...
is dog
测试完毕...
结束,准备退出测试
@Disabled
被
@Disabled
注解的方法不在参与测试,下面对
testIsDog
方法添加了
@Disabled
注解。
classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@BeforeEachpublicvoidstart(){System.out.println("开始测试...");}@Disabled("由于xx原因,关闭 testIsDog 测试")@TestpublicvoidtestIsDog(){String name ="dog";Assertions.assertEquals(name,"dog");System.out.println("is dog");}@TestpublicvoidtestIsCat(){String name ="cat";Assertions.assertEquals(name,"cat");System.out.println("is cat");}@AfterEachpublicvoidend(){System.out.println("测试完毕...");}@AfterAllpublicstaticvoidclose(){System.out.println("结束,准备退出测试");}}
这会输出:
初始化,准备测试信息
开始测试...
is cat
测试完毕...
由于xx原因,关闭 testIsDog 测试
结束,准备退出测试
@DisplayName
使用
@DisplayName
注解可以自定义测试方法的显示名称,下面为两个测试方法自定义名称。
classJUnitBeforeAll{@BeforeAllpublicstaticvoidinit(){System.out.println("初始化,准备测试信息");}@BeforeEachpublicvoidstart(){System.out.println("开始测试...");}@DisplayName("是否是狗")@Disabled@TestpublicvoidtestIsDog(){String name ="dog";Assertions.assertEquals(name,"dog");System.out.println("is dog");}@DisplayName("是否是猫")@TestpublicvoidtestIsCat(){String name ="cat";Assertions.assertEquals(name,"cat");System.out.println("is cat");}@AfterEachpublicvoidend(){System.out.println("测试完毕...");}@AfterAllpublicstaticvoidclose(){System.out.println("结束,准备退出测试");}}
在
idea
中运行后,可以看到配置的中文名称。
@ParameterizedTest
使用注解
@ParameterizedTest
结合
@ValueSource
,可以对不用的入参进行测试。下面的示例使用
@ParameterizedTest
来开始参数化单元测试,
name
属性用来定义测试名称,
@ValueSource
则定义了两个测试值。
importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.params.ParameterizedTest;importorg.junit.jupiter.params.provider.ValueSource;publicclassJUnitParam{//@Test@DisplayName("是否是狗")@ValueSource(strings ={"dog","cat"})@ParameterizedTest(name ="开始测试入参 {0} ")publicvoidtestIsDog(String name){Assertions.assertEquals(name,"dog");}}
这会输出:
@Order
在类上增加注解
@TestMethodOrder
,然后在方法上使用
@Order
指定顺序,数字越小优先级越搞,可以保证测试方法运行顺序。
importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.MethodOrderer.OrderAnnotation;importorg.junit.jupiter.api.Order;importorg.junit.jupiter.api.Test;importorg.junit.jupiter.api.TestMethodOrder;importorg.junit.jupiter.api.condition.EnabledOnJre;importstaticorg.junit.jupiter.api.condition.JRE.JAVA_19;@TestMethodOrder(OrderAnnotation.class)publicclassJUnitOrder{@Test@DisplayName("测试是否是狗")@Order(2)publicvoidtestIsDog(){String name ="dog";Assertions.assertEquals(name,"dog");System.out.println("is dog");}@DisplayName("是否是猫")@Test@Order(1)publicvoidtestIsCat(){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
中的一个方法;除此之外,还几乎包括了所有我们日常测试想要用到的判断方法。
下面是一些演示:
importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;publicclassJunitAssert{@DisplayName("是否是狗")@TestpublicvoidtestIsDog(){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("是否是猫")@TestpublicvoidtestIsCat(){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
.
packagecom.wdbyte.test.junit5;publicclassPerson{publicintgetLuckyNumber(){return7;}}
编写测试类 PersonTest 放在
src.test.java.com.wdbyte.test.junit5
. 这里判断获取到的幸运数字是否是 8 ,明显方法返回的是 7 ,所以这里是测试会报错。
packagecom.wdbyte.test.junit5;importorg.junit.jupiter.api.Assertions;importorg.junit.jupiter.api.DisplayName;importorg.junit.jupiter.api.Test;@DisplayName("测试 Presion")classPersonTest{@DisplayName("测试幸运数字")@TestvoidgetLuckyNumber(){Person person =newPerson();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。
版权归原作者 程序猿阿朗 所有, 如有侵权,请联系我们删除。