1. 概述

在本教程中,我们将深入探讨 JUnit 5 中基于注解的条件化测试执行机制

这些注解来自 JUnit Jupiter 扩展库condition 包,可以让我们根据不同的运行条件来控制测试是否执行。

⚠️ 注意:如果你的测试需要在特定环境、JDK 版本或系统配置下才跑,这些注解就是你的“神器”。


2. 操作系统条件控制

有时候我们需要让测试只在某些操作系统上运行,这时候就可以使用 @EnabledOnOs 注解。

使用方法很简单,只需要传入一个 OS 枚举值即可,也可以传入多个:

@Test
@EnabledOnOs({OS.WINDOWS, OS.MAC})
public void shouldRunBothWindowsAndMac() {
    // 只在 Windows 和 macOS 上运行
}

与之对应的是 @DisabledOnOs,用于禁止在指定系统上运行:

@Test
@DisabledOnOs(OS.LINUX)
public void shouldNotRunAtLinux() {
    // 在 Linux 上不运行
}

✅ 适用于不同平台行为差异较大的场景。


3. Java 运行时环境(JRE)条件控制

我们还可以根据 JRE 版本来控制测试是否运行,使用 @EnabledOnJre@DisabledOnJre 注解:

@Test
@EnabledOnJre({JRE.JAVA_10, JRE.JAVA_11})
public void shouldOnlyRunOnJava10And11() {
    // 只在 Java 10 和 11 上运行
}

从 JUnit 5.6 开始,新增了范围判断注解:

@Test
@EnabledForJreRange(min = JRE.JAVA_8, max = JRE.JAVA_13)
public void shouldOnlyRunOnJava8UntilJava13() {
    // Java 8 到 13 都可以运行
}

还有一个反向控制的:

@Test
@DisabledForJreRange(min = JRE.JAVA_14, max = JRE.JAVA_15)
public void shouldNotBeRunOnJava14AndJava15() {
    // Java 14 和 15 不运行
}

⚠️ 如果你想让测试只在主流版本上运行(比如 8、9、10、11),可以用 JRE.OTHER

@Test
@DisabledOnJre(JRE.OTHER)
public void thisTestOnlyRunsWithUpToDateJREs() {
    // 只在 Java 8~11 上运行
}

4. 系统属性条件控制

使用 @EnabledIfSystemProperty 可以根据 JVM 系统属性来控制测试执行:

@Test
@EnabledIfSystemProperty(named = "java.vm.vendor", matches = "Oracle.*")
public void onlyIfVendorNameStartsWithOracle() {
    // 只在 Oracle JVM 上运行
}

对应地,@DisabledIfSystemProperty 表示不满足条件时禁用:

@Test
@DisabledIfSystemProperty(named = "file.separator", matches = "[/]")
public void disabledIfFileSeperatorIsSlash() {
    // 文件分隔符为 / 时不运行(Linux/macOS)
}

5. 环境变量条件控制

类似地,可以使用 @EnabledIfEnvironmentVariable@DisabledIfEnvironmentVariable 来基于环境变量控制测试:

@Test
@EnabledIfEnvironmentVariable(named = "GDMSESSION", matches = "ubuntu")
public void onlyRunOnUbuntuServer() {
    // 只在 Ubuntu 桌面环境下运行
}

@Test
@DisabledIfEnvironmentVariable(named = "LC_TIME", matches = ".*UTF-8.")
public void shouldNotRunWhenTimeIsNotUTF8() {
    // LC_TIME 不是 UTF-8 格式时不运行
}

💡 如果你对系统属性和环境变量的区别不太清楚,可以参考 Java 中 System.getProperty 与 System.getenv 的区别


6. GraalVM 原生镜像条件控制

在使用 GraalVM 构建原生镜像时,可以使用以下两个注解控制测试是否在原生镜像环境中运行:

@Test
@EnabledInNativeImage
void shouldOnlyRunWithinNativeImage() {
    // 只在原生镜像中运行
}

@Test
@DisabledInNativeImage
void shouldNeverRunWithinNativeImage() {
    // 原生镜像中不运行
}

✅ 适用于 Spring Native 或其他需要 GraalVM 原生编译的项目。


7. 脚本驱动条件控制(⚠️ 已废弃)

7.1. 废弃说明

从 JUnit 5.5 开始,脚本驱动的条件注解(如 @EnabledIf@DisabledIf)被标记为废弃,并在 JUnit 5.6 中移除。

官方建议使用内置条件注解组合,或者自定义 ExecutionCondition 接口实现。

7.2. 脚本条件使用示例(仅适用于 JUnit 5.5 及以下)

虽然已废弃,但为了完整性,我们还是来看一下使用方式:

@Test
@EnabledIf("'FR' == systemProperty.get('user.country')")
public void onlyFrenchPeopleWillRunThisMethod() {
    // 只有法国用户运行
}
@Test
@DisabledIf("java.lang.System.getProperty('os.name').toLowerCase().contains('mac')")
public void shouldNotRunOnMacOS() {
    // macOS 不运行
}

还可以写多行脚本:

@Test
@EnabledIf(value = {
    "load('nashorn:mozilla_compat.js')",
    "importPackage(java.time)",
    "",
    "var thisMonth = LocalDate.now().getMonth().name()",
    "var february = Month.FEBRUARY.name()",
    "thisMonth.equals(february)"
},
    engine = "nashorn",
    reason = "On {annotation}, with script: {script}, result is: {result}")
public void onlyRunsInFebruary() {
    // 只在二月运行
}

脚本中可以使用的绑定变量包括:

  • systemEnvironment – 环境变量
  • systemProperty – 系统属性
  • junitConfigurationParameter – 配置参数
  • junitDisplayName – 显示名称
  • junitTags – 标签
  • anotherUniqueId – 唯一 ID

再来一个示例:

@Test
@DisabledIf("systemEnvironment.get('XPC_SERVICE_NAME') != null" +
        "&& systemEnvironment.get('XPC_SERVICE_NAME').contains('intellij')")
public void notValidForIntelliJ() {
    // IntelliJ IDEA 中不运行
}

⚠️ 虽然灵活,但脚本维护成本高,建议尽量使用内置注解替代。


8. 自定义条件注解

JUnit 5 支持我们创建自定义注解,将多个条件组合起来:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@DisabledOnOs({OS.WINDOWS, OS.SOLARIS, OS.OTHER})
@EnabledOnJre({JRE.JAVA_9, JRE.JAVA_10, JRE.JAVA_11})
@interface ThisTestWillOnlyRunAtLinuxAndMacWithJava9Or10Or11 {
}

@ThisTestWillOnlyRunAtLinuxAndMacWithJava9Or10Or11
public void someSuperTestMethodHere() {
    // 只在 Linux/macOS + Java 9~11 上运行
}

甚至可以结合脚本:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@DisabledIf("Math.random() >= 0.5")
@interface CoinToss {
}

@RepeatedTest(2)
@CoinToss
public void gamble() {
    // 大约 50% 的概率运行
}

✅ 自定义注解能极大提升代码复用性和可读性。


9. 总结

通过本文,我们了解了 JUnit 5 中各种条件化测试执行的注解,并通过示例展示了它们的使用方法。

我们还介绍了如何创建自定义注解来组合多个条件,从而更好地控制测试执行。

📚 更多详情请参考 JUnit 5 官方文档 - 条件化测试执行

📁 示例代码已上传至 GitHub 仓库


原始标题:JUnit 5 Conditional Test Execution with Annotations