Invocations of a @ParameterizedClass are indistinguishable in reports that show one name per test case
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:
- #5441 puts the class-invocation index into the legacy reporting name, so
junit-platform-reportingwritesmethodInParameterizedClass(String)[1][1]through[2][2]. - #5524 writes the display names of all ancestors into the
display-name:line of<system-out>, givingParameterizedClassNamesTest > [1] flag = true > methodInParameterizedClass(String) > [1] value = "x".
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 9c04c8085, JDK 21.0.9, compiled with -parameters, reproduced through Gradle 9.7.1 and through junit-platform-console-standalone.
Source: junit-team/junit-framework