This version is still in development and is not considered stable yet.
For the latest stable version, please use JUnit 6.0.1!

Conditional Test Execution

The ExecutionCondition extension API in JUnit Jupiter allows developers to either enable or disable a test class or test method based on certain conditions programmatically. The simplest example of such a condition is the built-in DisabledCondition which supports the @Disabled annotation (see Disabling Tests).

In addition to @Disabled, JUnit Jupiter also supports several other annotation-based conditions in the org.junit.jupiter.api.condition package that allow developers to enable or disable test classes and test methods declaratively. If you wish to provide details about why they might be disabled, every annotation associated with these built-in conditions has a disabledReason attribute available for that purpose.

When multiple ExecutionCondition extensions are registered, a test class or test method is disabled as soon as one of the conditions returns disabled. If a test class is disabled, all test methods within that class are automatically disabled as well. If a test method is disabled, that prevents execution of the test method and method-level lifecycle callbacks such as @BeforeEach methods, @AfterEach methods, and corresponding extension APIs. However, that does not prevent the test class from being instantiated, and it does not prevent the execution of class-level lifecycle callbacks such as @BeforeAll methods, @AfterAll methods, and corresponding extension APIs.

See ExecutionCondition and the following sections for details.

Composed Annotations

Note that any of the conditional annotations listed in the following sections may also be used as a meta-annotation in order to create a custom composed annotation. For example, the @TestOnMac annotation in the @EnabledOnOs demo shows how you can combine @Test and @EnabledOnOs in a single, reusable annotation.

Conditional annotations in JUnit Jupiter are not @Inherited. Consequently, if you wish to apply the same semantics to subclasses, each conditional annotation must be redeclared on each subclass.

Unless otherwise stated, each of the conditional annotations listed in the following sections can only be declared once on a given test interface, test class, or test method. If a conditional annotation is directly present, indirectly present, or meta-present multiple times on a given element, only the first such annotation discovered by JUnit will be used; any additional declarations will be silently ignored. Note, however, that each conditional annotation may be used in conjunction with other conditional annotations in the org.junit.jupiter.api.condition package.

Operating System and Architecture Conditions

A container or test may be enabled or disabled on a particular operating system, architecture, or combination of both via the @EnabledOnOs and @DisabledOnOs annotations.

Conditional execution based on operating system
@Test
@EnabledOnOs(MAC)
void onlyOnMacOs() {
	// ...
}

@TestOnMac
void testOnMac() {
	// ...
}

@Test
@EnabledOnOs({ LINUX, MAC })
void onLinuxOrMac() {
	// ...
}

@Test
@DisabledOnOs(WINDOWS)
void notOnWindows() {
	// ...
}

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Test
@EnabledOnOs(MAC)
@interface TestOnMac {
}
Conditional execution based on architecture
@Test
@EnabledOnOs(architectures = "aarch64")
void onAarch64() {
	// ...
}

@Test
@DisabledOnOs(architectures = "x86_64")
void notOnX86_64() {
	// ...
}

@Test
@EnabledOnOs(value = MAC, architectures = "aarch64")
void onNewMacs() {
	// ...
}

@Test
@DisabledOnOs(value = MAC, architectures = "aarch64")
void notOnNewMacs() {
	// ...
}

Java Runtime Environment Conditions

A container or test may be enabled or disabled on particular versions of the Java Runtime Environment (JRE) via the @EnabledOnJre and @DisabledOnJre annotations or on a particular range of versions of the JRE via the @EnabledForJreRange and @DisabledForJreRange annotations. The range effectively defaults to JRE.JAVA_8 as the lower bound and JRE.OTHER as the upper bound, which allows usage of half open ranges.

The following listing demonstrates the use of these annotations with predefined JRE enum constants.

@Test
@EnabledOnJre(JAVA_17)
void onlyOnJava17() {
	// ...
}

@Test
@EnabledOnJre({ JAVA_17, JAVA_21 })
void onJava17And21() {
	// ...
}

@Test
@EnabledForJreRange(min = JAVA_9, max = JAVA_11)
void fromJava9To11() {
	// ...
}

@Test
@EnabledForJreRange(min = JAVA_9)
void onJava9AndHigher() {
	// ...
}

@Test
@EnabledForJreRange(max = JAVA_11)
void fromJava8To11() {
	// ...
}

@Test
@DisabledOnJre(JAVA_9)
void notOnJava9() {
	// ...
}

@Test
@DisabledForJreRange(min = JAVA_9, max = JAVA_11)
void notFromJava9To11() {
	// ...
}

@Test
@DisabledForJreRange(min = JAVA_9)
void notOnJava9AndHigher() {
	// ...
}

@Test
@DisabledForJreRange(max = JAVA_11)
void notFromJava8To11() {
	// ...
}

Since the enum constants defined in JRE are static for any given JUnit release, you might find that you need to configure a Java version that is not supported by the JRE enum. For example, as of JUnit Jupiter 5.12 the JRE enum defines JAVA_25 as the highest supported Java version. However, you may wish to run your tests against later versions of Java. To support such use cases, you can specify arbitrary Java versions via the versions attributes in @EnabledOnJre and @DisabledOnJre and via the minVersion and maxVersion attributes in @EnabledForJreRange and @DisabledForJreRange.

The following listing demonstrates the use of these annotations with arbitrary Java versions.

@Test
@EnabledOnJre(versions = 26)
void onlyOnJava26() {
	// ...
}

@Test
@EnabledOnJre(versions = { 25, 26 })
// Can also be expressed as follows.
// @EnabledOnJre(value = JAVA_25, versions = 26)
void onJava25And26() {
	// ...
}

@Test
@EnabledForJreRange(minVersion = 26)
void onJava26AndHigher() {
	// ...
}

@Test
@EnabledForJreRange(minVersion = 25, maxVersion = 27)
// Can also be expressed as follows.
// @EnabledForJreRange(min = JAVA_25, maxVersion = 27)
void fromJava25To27() {
	// ...
}

@Test
@DisabledOnJre(versions = 26)
void notOnJava26() {
	// ...
}

@Test
@DisabledOnJre(versions = { 25, 26 })
// Can also be expressed as follows.
// @DisabledOnJre(value = JAVA_25, versions = 26)
void notOnJava25And26() {
	// ...
}

@Test
@DisabledForJreRange(minVersion = 26)
void notOnJava26AndHigher() {
	// ...
}

@Test
@DisabledForJreRange(minVersion = 25, maxVersion = 27)
// Can also be expressed as follows.
// @DisabledForJreRange(min = JAVA_25, maxVersion = 27)
void notFromJava25To27() {
	// ...
}

Native Image Conditions

A container or test may be enabled or disabled within a GraalVM native image via the @EnabledInNativeImage and @DisabledInNativeImage annotations. These annotations are typically used when running tests within a native image using the Gradle and Maven plug-ins from the GraalVM Native Build Tools project.

@Test
@EnabledInNativeImage
void onlyWithinNativeImage() {
	// ...
}

@Test
@DisabledInNativeImage
void neverWithinNativeImage() {
	// ...
}

System Property Conditions

A container or test may be enabled or disabled based on the value of the named JVM system property via the @EnabledIfSystemProperty and @DisabledIfSystemProperty annotations. The value supplied via the matches attribute will be interpreted as a regular expression.

@Test
@EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
void onlyOn64BitArchitectures() {
	// ...
}

@Test
@DisabledIfSystemProperty(named = "ci-server", matches = "true")
void notOnCiServer() {
	// ...
}

As of JUnit Jupiter 5.6, @EnabledIfSystemProperty and @DisabledIfSystemProperty are repeatable annotations. Consequently, these annotations may be declared multiple times on a test interface, test class, or test method. Specifically, these annotations will be found if they are directly present, indirectly present, or meta-present on a given element.

Environment Variable Conditions

A container or test may be enabled or disabled based on the value of the named environment variable from the underlying operating system via the @EnabledIfEnvironmentVariable and @DisabledIfEnvironmentVariable annotations. The value supplied via the matches attribute will be interpreted as a regular expression.

@Test
@EnabledIfEnvironmentVariable(named = "ENV", matches = "staging-server")
void onlyOnStagingServer() {
	// ...
}

@Test
@DisabledIfEnvironmentVariable(named = "ENV", matches = ".*development.*")
void notOnDeveloperWorkstation() {
	// ...
}

As of JUnit Jupiter 5.6, @EnabledIfEnvironmentVariable and @DisabledIfEnvironmentVariable are repeatable annotations. Consequently, these annotations may be declared multiple times on a test interface, test class, or test method. Specifically, these annotations will be found if they are directly present, indirectly present, or meta-present on a given element.

Custom Conditions

As an alternative to implementing an ExecutionCondition, a container or test may be enabled or disabled based on a condition method configured via the @EnabledIf and @DisabledIf annotations. A condition method must have a boolean return type and may accept either no arguments or a single ExtensionContext argument.

The following test class demonstrates how to configure a local method named customCondition via @EnabledIf and @DisabledIf.

@Test
@EnabledIf("customCondition")
void enabled() {
	// ...
}

@Test
@DisabledIf("customCondition")
void disabled() {
	// ...
}

boolean customCondition() {
	return true;
}

Alternatively, the condition method can be located outside the test class. In this case, it must be referenced by its fully qualified name as demonstrated in the following example.

package example;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIf;

class ExternalCustomConditionDemo {

	@Test
	@EnabledIf("example.ExternalCondition#customCondition")
	void enabled() {
		// ...
	}

}

class ExternalCondition {

	static boolean customCondition() {
		return true;
	}

}

There are several cases where a condition method would need to be static:

  • when @EnabledIf or @DisabledIf is used at class level

  • when @EnabledIf or @DisabledIf is used on a @ParameterizedTest or a @TestTemplate method

  • when the condition method is located in an external class

In any other case, you can use either static methods or instance methods as condition methods.

It is often the case that you can use an existing static method in a utility class as a custom condition.

For example, java.awt.GraphicsEnvironment provides a public static boolean isHeadless() method that can be used to determine if the current environment does not support a graphical display. Thus, if you have a test that depends on graphical support you can disable it when such support is unavailable as follows.

@DisabledIf(value = "java.awt.GraphicsEnvironment#isHeadless",
	disabledReason = "headless environment")