Annotation 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...
 }
 
Since:
6.0.0
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    Regular expressions that exclude matching entries from the parent's java.class.path when constructing the forked JVM's classpath.
    Names or prefix patterns of system properties from the parent JVM to propagate to the forked child.
    Raw JVM arguments to prepend to the forked JVM's command line, e.g.
    System properties to set on the forked JVM, each as a "key=value" string.
  • Element Details

    • systemProperties

      String[] systemProperties
      System properties to set on the forked JVM, each as a "key=value" string.
      Default:
      {}
    • jvmArgs

      String[] jvmArgs
      Raw JVM arguments to prepend to the forked JVM's command line, e.g. "-Xmx128m" or "--add-opens=java.base/java.lang=ALL-UNNAMED".
      Default:
      {}
    • inheritProperties

      String[] inheritProperties
      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.

      Default:
      {}
    • excludeFromClasspath

      String[] excludeFromClasspath
      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.

      Default:
      {}