JUnit 5 @ParamterizedTest {arguments} vs {argumentsWithNames} placeholders

Viewed 1203

I'm using JUnit 5.7.0, IntellijIDEA 2021.1.2 CE, MacOS Catalina 10.15. and don't understand the difference between ParameterizedTest.ARGUMENTS_PLACEHOLDER and ParameterizedTest.ARGUMENTS_WITH_NAMES_PLACEHOLDER.

According to the javadoc ARGUMENTS_WITH_NAMES_PLACEHOLDER deals with named arguments whereas ARGUMENTS_PLACEHOLDER with just arguments. The test result in IDEA for both tests looks the same:

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

@ParameterizedTest(name = ARGUMENTS_WITH_NAMES_PLACEHOLDER)
@CsvSource({"apple, 1", "banana, 2", "'lemon, lime', 3" })
void testWithArguments(String fruit, int rank) {
}

@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
@CsvSource({"apple, 1", "banana, 2", "'lemon, lime', 3" })
void testWithNamedArguments(String fruit, int rank) {
}

enter image description here

Can anyone provide an example where these two placeholders behave differently?

3 Answers

Solution

For ARGUMENTS_WITH_NAMES_PLACEHOLDER to work as expected, tests must be compiled with
-parameters flag. Then, argument names will be 1) stored, and 2) be able to be subsequently retrieved by JUnit via reflection.

In Practice

For Java projects built with Gradle, the following would enable this flag specifically for tests:

tasks.named('compileTestJava') {
    options.compilerArgs += '-parameters'
}

Reference

  • From Java 8 documentation on javac:

    -parameters Stores formal parameter names of constructors and methods in the generated class file so that the method java.lang.reflect.Executable.getParameters from the Reflection API can retrieve them.

  • Naturally, JUnit is using reflection to get parameter names. For instance, check out ParameterizedTestMethodContext#getParameterName (example from v5.7.1).

If you use @ParameterizedTest(name = ARGUMENTS_WITH_NAMES_PLACEHOLDER) you'll get output result with parameterName + "=" + paraveterValue.

But if you use @ParameterizedTest(name = ARGUMENTS_PLACEHOLDER) you'll get only paraveterValue

Also, you can see that in javadoc:

Placeholder for the complete, comma-separated named arguments list of the current invocation of a @ParameterizedTest method: {argumentsWithNames}

@API(status = EXPERIMENTAL, since = "5.6")
String ARGUMENTS_WITH_NAMES_PLACEHOLDER = "{argumentsWithNames}";

Default display name pattern for the current invocation of a @ParameterizedTest method: "[{index}] {argumentsWithNames}"
Note that the default pattern does not include the display name of the @ParameterizedTest method.

@API(status = EXPERIMENTAL, since = "5.3")
String DEFAULT_DISPLAY_NAME = "[" + INDEX_PLACEHOLDER + "] " + ARGUMENTS_WITH_NAMES_PLACEHOLDER;

I tried to run your code like that:

@ParameterizedTest(name = ARGUMENTS_WITH_NAMES_PLACEHOLDER)
@CsvSource({"apple, 1", "banana, 2", "'lemon, lime', 3" })
void testWithNamedArguments(String fruit, int rank) {
}

and I got the correct result:

enter image description here

I use jupiter 5.6.3, the feature ARGUMENTS_WITH_NAMES_PLACEHOLDER appeared since 5.6 version.

Regarding the -parameter option, if you stand still with Apache Maven,

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <configuration>
            <parameters>true</parameters>
          </configuration>
        </plugin>
Related