What happens
@ParameterizedClass
@ValueSource(booleans = {true, false})
class ParameterizedClassNamesTest {
ParameterizedClassNamesTest(boolean flag) {
}
@ParameterizedTest
@ValueSource(strings = {"x", "y"})
void methodInParameterizedClass(String value) {
}
}
Four test cases run. Compiled with -parameters, their display names are:
[1] value = "x"
[2] value = "y"
[1] value = "x"
[2] value = "y"
Nothing says whether a row ran with flag = true or with flag = false, so a report that shows one name per test case has two names for four cases. Here is Gradle's JUnit XML for exactly this class:
<testcase name="[1] value = "x"" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[2] value = "y"" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[1] value = "x"" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[2] value = "y"" classname="example.ParameterizedClassNamesTest"/>
When one of the four fails, the report does not say which. JUnit knows the value: it renders the class invocation as [1] flag = true on the container one level up. That level does not reach the name of the invocation.
Even junit.jupiter.params.displayname.default does not fix it
junit.jupiter.params.displayname.default = {displayName} [{index}] {argumentSetNameOrArgumentsWithNames}
gives
methodInParameterizedClass(String) [1] value = "x"
methodInParameterizedClass(String) [2] value = "y"
methodInParameterizedClass(String) [1] value = "x"
methodInParameterizedClass(String) [2] value = "y"
The method name is back and the class argument is still missing, because {displayName} expands to the enclosing method rather than to the class invocation, and no other placeholder reaches it. @ParameterizedClass(name = "...") names the class-invocation container, which a consumer that shows one name per test case does not see.
So a user of @ParameterizedClass has no way to make these four cases tell themselves apart, whatever they configure.
What I would like
The default display name of an invocation under a @ParameterizedClass distinguishes the class arguments, so that a build that configures nothing gets a readable report:
[1] flag = true > methodInParameterizedClass(String) [1] value = "x"
or any rendering that carries the same values. Changing a default changes what every existing build prints, so if that is too much, a placeholder for the enclosing invocation would at least make it configurable:
junit.jupiter.params.displayname.default = {enclosingDisplayName} > {displayName} [{index}] {argumentSetNameOrArgumentsWithNames}
Depth is the open question. A parameterized class also runs its @Nested classes, so an invocation can sit more than one level below the parameterized declaration, and a single-level placeholder would not always be enough. Joining the whole ancestry with > is what XmlReportWriter does since #5524, so that rendering would be consistent with what JUnit already writes elsewhere.
Why this is not a report-format issue
JUnit's own reports are unambiguous, and both fixes are recent:
I verified both on 6.1.3 and on main built from source.
The gap is for a consumer that has only the leaf display name. Gradle's JUnit XML report is one: it writes the display name into <testcase name>, drops the class-invocation container, and emits no unique id, so [1] value = "x" is all a reader gets. I filed that as gradle/gradle#39079, and Gradle can fix its side from the TestPlan without any change here. What Gradle cannot give me is a report that is readable by default, or a way to make it readable from my own build today.
Backport
If this is accepted, please consider releases/5.14.x as well. We are on the 5.x line and cannot move to 6 yet. #5524 was backported there and shipped in 5.14.4, so the branch is still taking changes in this area. @ParameterizedClass is still marked experimental, which I take to mean its display names are not frozen yet.
Versions
junit-jupiter 6.1.3 and main at commit 9c04c80, JDK 21.0.9, compiled with -parameters, reproduced through Gradle 9.7.1 and through junit-platform-console-standalone.
What happens
Four test cases run. Compiled with
-parameters, their display names are:Nothing says whether a row ran with
flag = trueor withflag = false, so a report that shows one name per test case has two names for four cases. Here is Gradle's JUnit XML for exactly this class:When one of the four fails, the report does not say which. JUnit knows the value: it renders the class invocation as
[1] flag = trueon the container one level up. That level does not reach the name of the invocation.Even
junit.jupiter.params.displayname.defaultdoes not fix itgives
The method name is back and the class argument is still missing, because
{displayName}expands to the enclosing method rather than to the class invocation, and no other placeholder reaches it.@ParameterizedClass(name = "...")names the class-invocation container, which a consumer that shows one name per test case does not see.So a user of
@ParameterizedClasshas no way to make these four cases tell themselves apart, whatever they configure.What I would like
The default display name of an invocation under a
@ParameterizedClassdistinguishes the class arguments, so that a build that configures nothing gets a readable report:or any rendering that carries the same values. Changing a default changes what every existing build prints, so if that is too much, a placeholder for the enclosing invocation would at least make it configurable:
Depth is the open question. A parameterized class also runs its
@Nestedclasses, so an invocation can sit more than one level below the parameterized declaration, and a single-level placeholder would not always be enough. Joining the whole ancestry with>is whatXmlReportWriterdoes since #5524, so that rendering would be consistent with what JUnit already writes elsewhere.Why this is not a report-format issue
JUnit's own reports are unambiguous, and both fixes are recent:
junit-platform-reportingwritesmethodInParameterizedClass(String)[1][1]through[2][2].display-name:line of<system-out>, givingParameterizedClassNamesTest > [1] flag = true > methodInParameterizedClass(String) > [1] value = "x".I verified both on 6.1.3 and on
mainbuilt from source.The gap is for a consumer that has only the leaf display name. Gradle's JUnit XML report is one: it writes the display name into
<testcase name>, drops the class-invocation container, and emits no unique id, so[1] value = "x"is all a reader gets. I filed that as gradle/gradle#39079, and Gradle can fix its side from theTestPlanwithout any change here. What Gradle cannot give me is a report that is readable by default, or a way to make it readable from my own build today.Backport
If this is accepted, please consider
releases/5.14.xas well. We are on the 5.x line and cannot move to 6 yet. #5524 was backported there and shipped in 5.14.4, so the branch is still taking changes in this area.@ParameterizedClassis still marked experimental, which I take to mean its display names are not frozen yet.Versions
junit-jupiter6.1.3 andmainat commit 9c04c80, JDK 21.0.9, compiled with-parameters, reproduced through Gradle 9.7.1 and throughjunit-platform-console-standalone.