Skip to content

Invocations of a @ParameterizedClass are indistinguishable in reports that show one name per test case #6041

Description

@vlsi

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 = &quot;x&quot;" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[2] value = &quot;y&quot;" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[1] value = &quot;x&quot;" classname="example.ParameterizedClassNamesTest"/>
<testcase name="[2] value = &quot;y&quot;" 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions