经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » JUnit » 查看文章
JUnit 5 单元测试教程
来源:cnblogs  作者:程序猿阿朗  时间:2022/11/19 17:14: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

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即可。

  1. <dependency>
  2. <groupId>org.junit.jupiter</groupId>
  3. <artifactId>junit-jupiter</artifactId>
  4. <version>5.9.1</version>
  5. <scope>test</scope>
  6. </dependency>

JUnit 5 常用注解

@Test

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

  1. import org.junit.jupiter.api.Assertions;
  2. import org.junit.jupiter.api.Test;
  3. /**
  4. * @author:https://www.wdbyte.com
  5. **/
  6. class JUnitTestIsDog {
  7. @Test
  8. public void testIsDog() {
  9. String name = "cat";
  10. Assertions.assertEquals(name, "dog");
  11. }
  12. }

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

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

  1. org.opentest4j.AssertionFailedError:
  2. Expected :cat
  3. Actual :dog
  4. <Click to see difference>

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

  1. @Test
  2. public void testIsDog2() {
  3. String name = "dog";
  4. Assertions.assertEquals(name, "dog");
  5. }

testIsDog2 方法测试通过。

@BeforeAll

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

  1. import org.junit.jupiter.api.Assertions;
  2. import org.junit.jupiter.api.BeforeAll;
  3. import org.junit.jupiter.api.BeforeEach;
  4. import org.junit.jupiter.api.Test;
  5. /**
  6. * @author:https://www.wdbyte.com
  7. **/
  8. class JUnitBeforeAll {
  9. @BeforeAll
  10. public static void init() {
  11. System.out.println("初始化,准备测试信息");
  12. }
  13. @Test
  14. public void testIsDog() {
  15. String name = "dog";
  16. Assertions.assertEquals(name, "dog");
  17. System.out.println("is dog");
  18. }
  19. @Test
  20. public void testIsCat() {
  21. String name = "cat";
  22. Assertions.assertEquals(name, "cat");
  23. System.out.println("is cat");
  24. }
  25. }

这会输出:

  1. 初始化,准备测试信息
  2. is cat
  3. is dog

@BeforeEach

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

  1. class JUnitBeforeAll {
  2. @BeforeAll
  3. public static void init() {
  4. System.out.println("初始化,准备测试信息");
  5. }
  6. @BeforeEach
  7. public void start(){
  8. System.out.println("开始测试...");
  9. }
  10. @Test
  11. public void testIsDog() {
  12. String name = "dog";
  13. Assertions.assertEquals(name, "dog");
  14. System.out.println("is dog");
  15. }
  16. @Test
  17. public void testIsCat() {
  18. String name = "cat";
  19. Assertions.assertEquals(name, "cat");
  20. System.out.println("is cat");
  21. }
  22. }

这会输出:

  1. 初始化,准备测试信息
  2. 开始测试...
  3. is cat
  4. 开始测试...
  5. is dog

@AfterAll

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

  1. class JUnitBeforeAll {
  2. @BeforeAll
  3. public static void init() {
  4. System.out.println("初始化,准备测试信息");
  5. }
  6. @BeforeEach
  7. public void start(){
  8. System.out.println("开始测试...");
  9. }
  10. @Test
  11. public void testIsDog() {
  12. //...
  13. }
  14. @Test
  15. public void testIsCat() {
  16. //...
  17. }
  18. @AfterAll
  19. public static void close() {
  20. System.out.println("结束,准备退出测试");
  21. }
  22. }

这会输出:

  1. 初始化,准备测试信息
  2. 开始测试...
  3. is cat
  4. 开始测试...
  5. is dog
  6. 结束,准备退出测试

@AfterEach

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

  1. class JUnitBeforeAll {
  2. @BeforeAll
  3. public static void init() {
  4. System.out.println("初始化,准备测试信息");
  5. }
  6. @BeforeEach
  7. public void start(){
  8. System.out.println("开始测试...");
  9. }
  10. @Test
  11. public void testIsDog() { //... }
  12. @Test
  13. public void testIsCat() { //... }
  14. @AfterEach
  15. public void end(){
  16. System.out.println("测试完毕...");
  17. }
  18. @AfterAll
  19. public static void close() {
  20. System.out.println("结束,准备退出测试");
  21. }
  22. }

这会输出:

  1. 初始化,准备测试信息
  2. 开始测试...
  3. is cat
  4. 测试完毕...
  5. 开始测试...
  6. is dog
  7. 测试完毕...
  8. 结束,准备退出测试

@Disabled

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

  1. class JUnitBeforeAll {
  2. @BeforeAll
  3. public static void init() {
  4. System.out.println("初始化,准备测试信息");
  5. }
  6. @BeforeEach
  7. public void start(){
  8. System.out.println("开始测试...");
  9. }
  10. @Disabled("由于xx原因,关闭 testIsDog 测试")
  11. @Test
  12. public void testIsDog() {
  13. String name = "dog";
  14. Assertions.assertEquals(name, "dog");
  15. System.out.println("is dog");
  16. }
  17. @Test
  18. public void testIsCat() {
  19. String name = "cat";
  20. Assertions.assertEquals(name, "cat");
  21. System.out.println("is cat");
  22. }
  23. @AfterEach
  24. public void end(){
  25. System.out.println("测试完毕...");
  26. }
  27. @AfterAll
  28. public static void close() {
  29. System.out.println("结束,准备退出测试");
  30. }
  31. }

这会输出:

  1. 初始化,准备测试信息
  2. 开始测试...
  3. is cat
  4. 测试完毕...
  5. 由于xx原因,关闭 testIsDog 测试
  6. 结束,准备退出测试

@DisplayName

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

  1. class JUnitBeforeAll {
  2. @BeforeAll
  3. public static void init() {
  4. System.out.println("初始化,准备测试信息");
  5. }
  6. @BeforeEach
  7. public void start() {
  8. System.out.println("开始测试...");
  9. }
  10. @DisplayName("是否是狗")
  11. @Disabled
  12. @Test
  13. public void testIsDog() {
  14. String name = "dog";
  15. Assertions.assertEquals(name, "dog");
  16. System.out.println("is dog");
  17. }
  18. @DisplayName("是否是猫")
  19. @Test
  20. public void testIsCat() {
  21. String name = "cat";
  22. Assertions.assertEquals(name, "cat");
  23. System.out.println("is cat");
  24. }
  25. @AfterEach
  26. public void end() {
  27. System.out.println("测试完毕...");
  28. }
  29. @AfterAll
  30. public static void close() {
  31. System.out.println("结束,准备退出测试");
  32. }
  33. }

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

@ParameterizedTest

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

  1. import org.junit.jupiter.api.Assertions;
  2. import org.junit.jupiter.api.DisplayName;
  3. import org.junit.jupiter.params.ParameterizedTest;
  4. import org.junit.jupiter.params.provider.ValueSource;
  5. public class JUnitParam {
  6. //@Test
  7. @DisplayName("是否是狗")
  8. @ValueSource(strings = {"dog", "cat"})
  9. @ParameterizedTest(name = "开始测试入参 {0} ")
  10. public void testIsDog(String name) {
  11. Assertions.assertEquals(name, "dog");
  12. }
  13. }

这会输出:

@Order

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

  1. import org.junit.jupiter.api.Assertions;
  2. import org.junit.jupiter.api.DisplayName;
  3. import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
  4. import org.junit.jupiter.api.Order;
  5. import org.junit.jupiter.api.Test;
  6. import org.junit.jupiter.api.TestMethodOrder;
  7. import org.junit.jupiter.api.condition.EnabledOnJre;
  8. import static org.junit.jupiter.api.condition.JRE.JAVA_19;
  9. @TestMethodOrder(OrderAnnotation.class)
  10. public class JUnitOrder{
  11. @Test
  12. @DisplayName("测试是否是狗")
  13. @Order(2)
  14. public void testIsDog() {
  15. String name = "dog";
  16. Assertions.assertEquals(name, "dog");
  17. System.out.println("is dog");
  18. }
  19. @DisplayName("是否是猫")
  20. @Test
  21. @Order(1)
  22. public void testIsCat() {
  23. String name = "cat";
  24. Assertions.assertEquals(name, "cat");
  25. System.out.println("is cat");
  26. }
  27. }

这会输出:

  1. is cat
  2. 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 中的一个方法;除此之外,还几乎包括了所有我们日常测试想要用到的判断方法。

下面是一些演示:

  1. import org.junit.jupiter.api.Assertions;
  2. import org.junit.jupiter.api.DisplayName;
  3. import org.junit.jupiter.api.Test;
  4. public class JunitAssert {
  5. @DisplayName("是否是狗")
  6. @Test
  7. public void testIsDog() {
  8. String name = "dog";
  9. Assertions.assertNotNull(name);
  10. Assertions.assertEquals(name, "dog");
  11. Assertions.assertNotEquals(name, "cat");
  12. Assertions.assertTrue("dog".equals(name));
  13. Assertions.assertFalse("cat".equals(name));
  14. }
  15. @DisplayName("是否是猫")
  16. @Test
  17. public void testIsCat() {
  18. String name = "cat";
  19. Assertions.assertNull(name, "name is not null");
  20. }
  21. }

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

这会输出:

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

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

Maven JUnit 测试

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

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

  1. package com.wdbyte.test.junit5;
  2. public class Person {
  3. public int getLuckyNumber() {
  4. return 7;
  5. }
  6. }

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

  1. package com.wdbyte.test.junit5;
  2. import org.junit.jupiter.api.Assertions;
  3. import org.junit.jupiter.api.DisplayName;
  4. import org.junit.jupiter.api.Test;
  5. @DisplayName("测试 Presion")
  6. class PersonTest {
  7. @DisplayName("测试幸运数字")
  8. @Test
  9. void getLuckyNumber() {
  10. Person person = new Person();
  11. Assertions.assertEquals(8, person.getLuckyNumber());
  12. }
  13. }

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

  1. <build>
  2. <plugins>
  3. <plugin>
  4. <artifactId>maven-surefire-plugin</artifactId>
  5. <version>2.22.2</version>
  6. </plugin>
  7. <plugin>
  8. <artifactId>maven-failsafe-plugin</artifactId>
  9. <version>2.22.2</version>
  10. </plugin>
  11. </plugins>
  12. </build>

执行测试命令:mvn test

  1. ? junit5-jupiter-starter git:(master) ? mvn test
  2. [INFO] Scanning for projects...
  3. [INFO] ....
  4. [INFO] -------------------------------------------------------
  5. [INFO] T E S T S
  6. [INFO] -------------------------------------------------------
  7. [INFO] Running com.wdbyte.test.junit5.PersonTest
  8. [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.031 s <<< FAILURE! - in com.wdbyte.test.junit5.PersonTest
  9. [ERROR] getLuckyNumber Time elapsed: 0.026 s <<< FAILURE!
  10. org.opentest4j.AssertionFailedError: expected: <8> but was: <7>
  11. at com.wdbyte.test.junit5.PersonTest.getLuckyNumber(PersonTest.java:18)
  12. [INFO]
  13. [INFO] Results:
  14. [INFO]
  15. [ERROR] Failures:
  16. [ERROR] PersonTest.getLuckyNumber:18 expected: <8> but was: <7>
  17. [INFO]
  18. [ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
  19. [INFO]
  20. [INFO] ------------------------------------------------------------------------
  21. [INFO] BUILD FAILURE
  22. [INFO] ------------------------------------------------------------------------
  23. [INFO] Total time: 1.777 s
  24. [INFO] Finished at: 2022-11-17T23:01:09+08:00
  25. [INFO] ------------------------------------------------------------------------

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

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

<完>

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

原文链接:https://www.cnblogs.com/niumoo/p/16902100.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号