Junit5: Introduce support for configuration parameters in console launcher and build plugins

Created on 12 Aug 2017  路  13Comments  路  Source: junit-team/junit5

Overview

As discussed in https://github.com/junit-team/junit5/issues/905#issuecomment-322001390, there is currently no native support for setting a _configuration parameter_ via the console launcher, Maven Surefire plugin, or Gradle plugin.

Thus, the only option from the build perspective is to set a system property or rely on the presence of a JUnit Platform _configuration file_ (see #1003).

For the JUnit Platform Gradle Plugin, you can set such a system property via a work-around that is discussed in https://github.com/junit-team/junit5/issues/475#issuecomment-242038043.

Proposal

ConsoleLauncher

Introduce a --config command line option that expects a key=value pair.

java -jar junit-platform-console-standalone-*.jar \
     --config junit.jupiter.extensions.autodetection.enabled=true \
     --config junit.jupiter.testinstance.lifecycle.default=per_class

Gradle

Align with the syntax supported for JVM system properties by Gradle tasks:

systemProperty('java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager')

This gives us:

junitPlatform {
  // ...
  configurationParameter('junit.jupiter.extensions.autodetection.enabled', 'true')
  configurationParameter('junit.jupiter.testinstance.lifecycle.default', 'per_class')
  // ...
}

Note, however, that the parentheses are optional in Groovy resulting in the following alternative syntax being supported out of the box.

junitPlatform {
  // ...
  configurationParameter 'junit.jupiter.extensions.autodetection.enabled', 'true'
  configurationParameter 'junit.jupiter.testinstance.lifecycle.default', 'per_class'
  // ...
}

Maven

Surefire does not support nested structures within properties, but that's not an issue if we take inspiration from Spring's support for inlined Properties in XML configuration, resulting in the following.

// ...
<build>
    <plugins>
        // ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <properties>
                    <excludeTags>integration, regression</excludeTags>
                    <configurationParameters>
                      junit.jupiter.extensions.autodetection.enabled = true
                      junit.jupiter.testinstance.lifecycle.default = per_class
                    </configurationParameters>
                </properties>
            </configuration>
            <dependencies>
                // ...
            </dependencies>
        </plugin>
    </plugins>
</build>
// ...

For an example of how the configurationParameters can be converted to a Map<String, String> with full support for the java.util.Properties syntax, see https://github.com/junit-team/junit5/issues/1015#issuecomment-323584880.

Deliverables

  • [x] Introduce support for setting configuration parameters in the console launcher.
  • [x] Introduce support for setting configuration parameters in the JUnit Platform Gradle Plugin.
  • [x] Introduce support for setting configuration parameters in the Maven Surefire Plugin.
  • [x] Update User Guide.
  • [x] Update release notes.
  • [X] ~Prepare sample projects.~
Gradle Maven Surefire Platform build enhancement

Most helpful comment

I'm in favor of both the alternative proposals for Gradle and Maven.

Thanks, @sbrannen! 馃憤

All 13 comments

I've updated the issue description with proposals for Console Launcher, Gradle, and Maven.

ConsoleLauncher

java -jar junit-platform-console-standalone-*.jar \
     --config junit.jupiter.extensions.autodetection.enabled=true \
     --config junit.jupiter.testinstance.lifecycle.default=PER_CLASS

Gradle

junitPlatform {
  // ...
  configurationParameters = [
    'junit.jupiter.extensions.autodetection.enabled': 'true',
    'junit.jupiter.testinstance.lifecycle.default': 'PER_CLASS'
  ]
  // ...
}

OR

junitPlatform {
  // ...
  configurationParameters['junit.jupiter.extensions.autodetection.enabled'] = 'true'
  configurationParameters['junit.jupiter.testinstance.lifecycle.default'] = 'PER_CLASS'
  // ...
}

Maven

Since Surefire does not support nested structures within properties our best option seems to be to do nothing and rely on the existing support for system properties.

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <systemPropertyVariables>
                    <junit.jupiter.extensions.autodetection.enabled>true</junit.jupiter.extensions.autodetection.enabled>
                    <junit.jupiter.testinstance.lifecycle.default>PER_CLASS</junit.jupiter.testinstance.lifecycle.default>
                </systemPropertyVariables>
                <properties>
                    <excludeTags>integration, regression</excludeTags>
                </properties>
            </configuration>
            <dependencies>
                ...
            </dependencies>
        </plugin>
    </plugins>
</build>
...

Proposal Review

ConsoleLauncher

I think --config is perfect.

Gradle

I think we should follow the same syntax supported for JVM system properties by Gradle tasks:

systemProperty('java.util.logging.manager', 'org.apache.logging.log4j.jul.LogManager')

This would give us:

junitPlatform {
  // ...
  configurationParameter('junit.jupiter.extensions.autodetection.enabled', 'true')
  configurationParameter('junit.jupiter.testinstance.lifecycle.default', 'per_class')
  // ...
}

Maven

I'm not a Surefire expert, but I honestly do not like relying on JVM system properties for this (unless there is literally no alternative). Rationale: system properties are technically not a first-class citizen in ConfigurationParameters and would never show up in invocations of toString() (e.g., while debugging or if we later decide to log the _contents_ of ConfigurationParameters at start-up (which I think is actually a good idea)).

I've updated the issue description with proposals for Console Launcher, Gradle, and Maven.

Thanks, @marcphilipp! 馃憤

Note that the parentheses are optional in my proposal for Gradle:

  // configurationParameter('junit.jupiter.extensions.autodetection.enabled', 'true')
  configurationParameter 'junit.jupiter.extensions.autodetection.enabled', 'true'

Alternative Proposal for Maven

Since Surefire does not support nested structures within properties our best option seems to be to do nothing and rely on the existing support for system properties.

We don't actually need a nested structure for this use case.

Rather, we can take inspiration from Spring's support for inlined Properties in XML configuration, resulting in the following.

...
<build>
    <plugins>
        ...
        <plugin>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <properties>
                    <excludeTags>integration, regression</excludeTags>
                    <configurationParameters>
                      junit.jupiter.extensions.autodetection.enabled = true
                      junit.jupiter.testinstance.lifecycle.default = per_class
                    </configurationParameters>
                </properties>
            </configuration>
            <dependencies>
                ...
            </dependencies>
        </plugin>
    </plugins>
</build>
...

It's then straightforward to read in the String content of the configurationParameters key in the map returned by org.apache.maven.surefire.providerapi.ProviderParameters.getProviderProperties() and load a Properties object by treating the entire content as an in-memory file. The contents of the Properties object can then be converted into a Map<String,String> which is then passed to the Launcher API. That could look something like the following (authored in the web browser and therefore not compiled or tested).

Map<String, String> configParams = new HashMap<>();

String content = this.parameters.getProviderProperties().get("configurationParameters");

try (StringReader reader = new StringReader(content))  {
    Properties props = new Properties();
    props.load(reader);
    props.forEach((key, value) -> configParams.put(String.valueOf(key), String.valueOf(value)));
}
catch (IOException ex) {
    // TODO Log warning.
}

// pass configParams to LauncherDiscoveryRequestBuilder.configurationParameters()

I'm in favor of both the alternative proposals for Gradle and Maven.

Thanks, @sbrannen! 馃憤

Update: replaced original proposals in this issue's description with my alternative proposals.

_in progress_

@marcphilipp,

Just out of curiosity... what do you plan to do regarding the new "Prepare sample projects" deliverable?

I thought it might be a good idea to update the Maven and Gradle consumer projects to use the new properties. Do you think that's not necessary?

I thought it might be a good idea to update the Maven and Gradle consumer projects to use the new properties. Do you think that's not necessary?

I think it could be a good idea to add the configuration examples to the build scripts but comment them out.

In other words, I wouldn't turn on any of the power features in those example apps. 馃槈

Now that I think about it, it's not that crucial and cannot be done before we release RC3... Thus, I'll remove that item from Deliverables for now.

Fixed in #1029.

Was this page helpful?
0 / 5 - 0 ratings