@Documented
@Incubating
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@ExtendWith(ForkedJvmExtension.class)
public @interface ForkedJvm
Runs the annotated test method (or every test method on the annotated class) in a freshly forked JVM, optionally configured with extra system properties and JVM arguments.
Useful for testing behaviour gated by JVM startup state that cannot be
toggled at runtime, e.g. static final fields initialised from
System.getProperty(...) at class load time
(such as groovy.val.enabled).
System properties are supplied as "key=value" strings:
@Test
@ForkedJvm(systemProperties = {"groovy.val.enabled=false"})
void runsInChildJvm() {
assert System.getProperty("groovy.val.enabled").equals("false");
}
JVM arguments are passed verbatim:
@Test
@ForkedJvm(jvmArgs = {"--add-opens=java.base/java.lang=ALL-UNNAMED"})
void withModuleAccess() { ... }
Recipe — @GrabConfig(systemClassLoader=true) tests:
scripts that use @GrabConfig(systemClassLoader=true) to add
resolved JARs to the JVM system classloader only behave
correctly when that classloader is Groovy's RootLoader, which
cannot be retrofitted after JVM startup. Set it via jvmArgs:
@Test
@ForkedJvm(jvmArgs = {"-Djava.system.class.loader=org.codehaus.groovy.tools.RootLoader"})
void scriptUsesGrabConfigSystemClassLoader() {
assertScript '''
@GrabConfig(systemClassLoader=true)
@Grab('org.example:some-lib:1.0')
// ... library is now visible to anything resolving via the system classloader ...
'''
}
Properties from the parent JVM can be propagated to the child via
inheritProperties(). Each entry is either an exact property
name or a prefix pattern ending in *. This is useful when the
parent test JVM is configured by the build (e.g. Gradle) with a
property the child also needs, such as Spock's Groovy-version-check
override:
@Test
@ForkedJvm(inheritProperties = {"spock.*"})
void seesSpockOverride() { ... }
Lifecycle gotcha: JUnit lifecycle hooks
(@BeforeAll, @BeforeEach, @AfterAll,
@AfterEach) fire in both the parent and the forked
child JVM, because the child re-runs the class lifecycle for the
targeted method. Setup that mutates JVM-global state — typically
System.setProperty(...) — therefore replays in the child and
can defeat tests that assert on what was (or was not) propagated
across the fork. Guard parent-only setup with the forked-flag check:
@BeforeAll
static void setUp() {
if (Boolean.parseBoolean(System.getProperty("groovy.junit6.forked"))) return;
// ...parent-only setup here...
}
| Type | Name and Description |
|---|---|
String[] |
excludeFromClasspathRegular expressions that exclude matching entries from the parent's java.class.path when constructing the forked JVM's classpath.
|
String[] |
inheritPropertiesNames or prefix patterns of system properties from the parent JVM to propagate to the forked child. |
String[] |
jvmArgsRaw JVM arguments to prepend to the forked JVM's command line, e.g. |
String[] |
systemPropertiesSystem properties to set on the forked JVM, each as a "key=value" string.
|
Regular expressions that exclude matching entries from the parent's
java.class.path when constructing the forked JVM's classpath.
Each pattern is applied to each path entry with
Matcher.find, so plain substrings (e.g.
"junit-platform-instrumentation") work without anchoring.
Patterns can also be supplied via the system property
groovy.junit6.forked.excludeClasspath (comma-separated) for
build- or CI-level configuration without modifying test source.
Annotation values and the system-property values are unioned.
Limitations: this filter only inspects entries appearing literally
in java.class.path. Modules on --module-path,
directory wildcards (e.g. lib/*), and Class-Path:
manifest entries inherited from another jar are not matched.
Names or prefix patterns of system properties from the parent JVM to
propagate to the forked child. Each entry is either an exact property
name (e.g. "spock.iKnowWhatImDoing.disableGroovyVersionCheck")
or a prefix pattern ending in * (e.g. "spock.*" or
"groovy.compiler.*"). Patterns that match no parent property
are silently ignored.
Properties supplied via systemProperties() override inherited values for the same key.
Raw JVM arguments to prepend to the forked JVM's command line, e.g.
"-Xmx128m" or "--add-opens=java.base/java.lang=ALL-UNNAMED".
System properties to set on the forked JVM, each as a "key=value" string.