【JUnit实战3_02】第二章:探索 JUnit 的核心机制(一)

JUnit in Action, Third Edition

《JUnit in Action》全新第3版封面截图

写在前面
由于《JUnit in Action》这本大部头实战资料的目标受众是对 JUnit 的零基础人士,所以在介绍完第一章的开胃菜后,第二章就可以开始扫盲了。本章针对 JUnit 5 的核心特性做了一个相对全面的拉网式概述,让大家直观感受一下这个单测框架目前所具备的能力。由于知识点较多,拟根据篇幅拆分为多个小节进行梳理,本节为第二章内容的第一篇。

第二章 探索 JUnit 的核心功能

本章概要

  • JUnit 生命周期
  • JUnit 的核心类、核心方法、核心注解
  • JUnit 运行机制演示

2.1 相关资源

2.2 测试类与测试方法

一个测试类(test class)可以是顶级类、静态成员类,或是使用 @Nested 注解的内部类,其中包含一个或多个测试方法。

测试类不能是抽象的,且必须提供单一的无参构造方法(如果确实需要参数,也可以通过运行时依赖注入的方式引入)。

测试类的在 JUnit 5 中的可见性可最低降至 包路级私有(package-private,即不加任何访问修饰符,保持默认的 package 包级可见即可 1;此前 JUnit 4 必须声明为 public

测试方法也不能是抽象方法,并且不能有返回值(统一返回 void)。

按照加注注解的不同,可分为两大类:(普通)测试方法(test method)和生命周期方法(life cycle method),其中——

测试方法 是加注了如下注解的实例方法:

  • @Test
  • @RepeatedTest
  • @ParameterizedTest
  • @TestFactory
  • @TestTemplate

生命周期方法 是加注了如下注解的方法:

  • @BeforeAll
  • @AfterAll
  • @BeforeEach
  • @AfterEach

2.3 本章示例代码的运行方法

首先添加 Maven 依赖:

<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.0</version>
<scope>test</scope>
</dependency>

然后在命令行执行命令:

mvn test -Dtest=SpecificTestClassName

本地实测时由于手动安装并配置了 Java11,后续通过 IDEA 又安装了 Java21Java24 等多个版本,导致上述命令报错,提示编译的版本不一致:

> mvn test
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.manning.junitbook:ch02-core >-------------------
  [INFO] Building ch02-core 1.0-SNAPSHOT
  [INFO] --------------------------------[ jar ]---------------------------------
  [INFO]
  [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ ch02-core ---
  [INFO] Using 'UTF-8' encoding to copy filtered resources.
  [INFO] skip non existing resourceDirectory E:\workspaces\juia3\ch02-core\src\main\resources
  [INFO]
  [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ ch02-core ---
  [INFO] Changes detected - recompiling the module!
  [INFO] Compiling 19 source files to E:\workspaces\juia3\ch02-core\target\classes
  [INFO] ------------------------------------------------------------------------
  [INFO] BUILD FAILURE
  [INFO] ------------------------------------------------------------------------
  [INFO] Total time:  0.478 s
  [INFO] Finished at: 2025-10-09T16:48:42+08:00
  [INFO] ------------------------------------------------------------------------
  [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project ch02-core: Fatal error compiling: 错误: 无效的目标发行版:1.11 -> [Help 1]
  [ERROR]
  [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
  [ERROR] Re-run Maven using the -X switch to enable full debug logging.
  [ERROR]
  [ERROR] For more information about the errors and possible solutions, please read the following articles:
  [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
  >

解决方法:新增 Maven 插件手动指定编译版本为 Java11,与本机系统保持一致:

<build>
  <plugins>
    <!-- snip -->
      <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.8.1</version>
        <configuration>
        <source>11</source>
        <target>11</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

2.4 关于 @TestInstance 注解

一般情况下,JUnit 在调用每个 @Test 注解过的方法前会创建一个该测试类的新实例,以确保测试方法的独立性。

如果想让 @BeforeAll 注解的方法实现 只运行一次、然后再运行其余 @Test 用例,通常需要声明为 static 静态方法;否则,就必须在测试类上加注 @TestInstance 注解,并传入参数 TestInstance.Lifecycle.PER_CLASS

// 1. use static
class SUTTest {
// -- snip --
@BeforeAll
static void setUpClass() {
resourceForAllTests = new ResourceForAllTests("Our resource for all tests");
}
// -- snip --
}
// 2. use @TestInstance annotation
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class SUTTest {
// -- snip --
@BeforeAll
void setUpClass() {
resourceForAllTests = new ResourceForAllTests("Our resource for all tests");
}
// -- snip --
}

2.5 @DisplayName 注解

该注解方便 IDEAIDE 工具增强测试用例运行结果的可读性:

fig2.1

该注解的参数值支持文本、特殊字符、甚至是 emoji 表情符号,非常强大。

根据 IntelliJ IDEA 官方发布的 教学视频,可以创建自定义模板快速输入带 @DisplayName 注解的代码块:

Fig2.2

另外,编辑变量可实现 TEST_NAMEMETHOD_NAME 的联动:

Fig2.3

实测备忘录

在测试类上加 @Disabled 注解后,直接运行该测试类,里面的方法仍然会执行。

要让 IDEA 不执行该测试类,只能 从对应的包上 运行单元测试:

> mvn test -Dtest="com/manning/junitbook/ch02/disabled/*"

  1. JUnit 5 团队认为,测试代码是实现细节的一部分,通常不需要被外部代码所调用。将测试类的可见性降至包私有,更符合封装的原则,使得代码更加简洁。 ↩︎

posted @ 2025-11-11 20:44  gccbuaa  阅读(6)  评论(0)    收藏  举报