gradle / gradle

Adaptable, fast automation for all

Home Page:https://gradle.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JUnit 5 Dynamic tests not showing with their displayName

johnzielke opened this issue · comments

Expected Behavior

When executing dynamic tests with @testfactory, it should use the displayname of the dynamic test in the report. For example this is the image I get in IntelliJ if I run directly from the IDE using rightclick on the classes an "Run Test.."
image

Current Behavior

When running the tests with "gradle test" in IntelliJ, it shows the following:
image
Every test is just indexed

Context

This is not ideal, as I can't see what data I used in that specific test

Steps to Reproduce (for bugs)

I am using the following build.gradle:

buildscript {
	ext {
		kotlinVersion = '1.2.51'
		springBootVersion = '2.0.3.RELEASE'
		junitJupiterVersion  = '5.2.0'
		junitPlatformVersion = '1.2.0'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
		classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
		classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
		classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.6'
	}
}


apply plugin: 'kotlin'
apply plugin: 'kotlin-spring'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
apply plugin: 'com.google.protobuf'
apply plugin: 'idea'

protobuf {
	protoc {
		artifact = "com.google.protobuf:protoc:3.5.1-1"
	}
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
compileKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}
compileTestKotlin {
	kotlinOptions {
		freeCompilerArgs = ["-Xjsr305=strict"]
		jvmTarget = "1.8"
	}
}

repositories {
	mavenCentral()
	jcenter()
	maven { url 'https://jitpack.io' }
}
test {
	useJUnitPlatform()
}

dependencies {
	compile('org.springframework.boot:spring-boot-starter-amqp')
	compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
	compile "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
	testCompile('org.springframework.boot:spring-boot-starter-test'){
		exclude group: 'junit', module: 'junit'
	}

	testImplementation("org.junit.jupiter:junit-jupiter-api:${junitJupiterVersion}")
        testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:${junitJupiterVersion}")


	compile group: 'com.google.protobuf', name: 'protobuf-java-util', version: '3.6.0'
	compile 'net.andreinc.mockneat:mockneat:0.1.7'
	compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
	compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
	compile 'org.optaplanner:optaplanner-core:7.7.0.Final'
	compile group: 'com.google.guava', name: 'guava', version: '20.0'
}

And this is a example from my test:
image

Your Environment

Gradle 4.8.1
IntelliJ 2018.1.5
Windows 7
Java 8

I think this issue is comprised of two parts: (1) Gradle's not reporting the hierarchical structure (see #4438), and (2) the display names are not exposed via the Tooling API.

@blindpirate Were there plans to add that as a follow-up on #4423?

Not currently. We just don't prioritize this issue, but it should definitely be improved.

Hi ,

I am using ant and Junit 5 .I have observed the same issue of the nested dynamic test names not shown .
The Report is currently shown as

Test: deploymentTestWithContainers()[1][1][1] took 1 sec(s)
Test: deploymentTestWithContainers()[1][1][2] took 832 milli sec(s)
Test: deploymentTestWithContainers()[2][1][1] took 20 sec(s)
Test: deploymentTestWithContainers()[2][1][2] took 20 sec(s)

where deploymentTestWithContainers() is the method name which is returning the stream of dynamic nodes.
So wanted to check if this issue is still open with ant also or is this issue specific to Gradle?

This feature of bringing the test names would help us integrate JUNIT5 with the Test Tracking System by enabling us to update the tests from this JUnit 5 Report.

Please let me know If I need to specify any other details in this context.

Thanks

I also have this problem.

Gradle: 4.10.2-all
Junit5: 5.1.1

IntelliJ Idea works fine.

I've also run into this issue. (See the link above)

This issue is seen with ant and dynamic tests.

Note, this is also occurring on ParameterizedTests.

Note, this is also occurring on ParameterizedTests.

With gradle 5.1.1 and Junit 5.3.2 the HTML report shows the correct parameterized test names, but the xml test results file shows the incorrect
<testcase name="test(String, String, int, int, int, int)[1]" classname="hu.mv.task.DistributionCheckTest" time="0.017"/>
method name string insted of the test name.
The binary/results.bin file contains the test name, along with the method name "test(String, String, int, int, int, int)[1]" separated by a '¦' character

The "legacy" XML format does not support multiple names per test. Traditionally, the name attribute was interpreted by many tools as the method name for navigation etc. What we'd really need is a new reporting format that supports this and other new features (i.e. source location, additional reporting entries). I plan to pick up this feature as part of my work on JUnit in one of the next releases. Feel free to follow ota4j-team/opentest4j#9 if you're interested in its progress (which has stalled for the last couple of months).

Is there anything we can do about this? Idea is using gradle as default build tool and to run tests in the 2019.1 EAP version, so now this also affects test runs inside the IDE.

This issue is causing us to duplicate the intended name of each test in each assertion's exception message. It's not ideal, but necessary to workaround this issue.

+1 for this issue, we are also "suffering" from this (using Gradle 5.2.1).

Are there at the moment any concrete plans to properly support JUnit 5 dynamic tests, nesting, etc in gradle @marcphilipp ?

As much as it pains me to say it, I'm afraid there are no short-term plans to add support for nesting.

The way I made it work is by running it with Junit4 (do not put useJUnitPlatform() in the test {} section) and by using the annotation @RunWith(JUnitPlatform.class), the result is then as expected.

I can partially confirm that with junit4 it works better, but still not ideal.

Considering following example running with plain Junit5 and Jupiter:

import org.junit.Assert.assertEquals
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory
import java.util.*

class JunitPOC {

  @TestFactory
  fun tests(): Collection<DynamicTest> {
    return Arrays.asList(
      DynamicTest.dynamicTest("Test name A") { assertEquals(2, 1 + 3) },
      DynamicTest.dynamicTest("Test name B") { assertEquals(4, 2 + 2) })
  }
}

I get:

> Task :api_tests:test FAILED

io.kotlintest.JunitPOC > tests()[1] FAILED
    java.lang.AssertionError

io.kotlintest.JunitPOC > tests()[2] PASSED

while for the junit4 version:

import org.junit.Assert.assertEquals
import org.junit.jupiter.api.DynamicTest
import org.junit.jupiter.api.TestFactory
import org.junit.platform.runner.JUnitPlatform
import org.junit.runner.RunWith
import java.util.*

@RunWith(JUnitPlatform::class)
class JunitPOC {
   ...
}

i get:

io.kotlintest.JunitPOC > ).Test name A(tests FAILED
    java.lang.AssertionError

io.kotlintest.JunitPOC > ).Test name B(tests PASSED

io.kotlintest.JunitPOC > .tests SKIPPED

3 tests completed, 1 failed, 1 skipped

Gradle dependencies:

    testImplementation "org.junit.platform:junit-platform-runner:1.4.1"
    testImplementation "org.junit.jupiter:junit-jupiter-engine:5.3.1"

As you can see, names are a malformed a bit and mystrious SKIPPED test has appeared.

Both versions are working pretty well under IntelliJ 2019.1

This makes test reporting really annoying when using libraries like https://github.com/dmcg/minutest

Struggling with a very similar issue. When I run JUnit 5 tests with the Platform Test Runner in IntelliJ Ultimate 2019.1 with Gradle 4.10.2, I can see display names and the names of parameterised tests. When I choose the Gradle Test Runner, the IDE displays class and method names.
That said, I am not using a test factory. It's just plain JUnit 5 tests annotated with @Test and @DisplayName or @ParameterizedTest with name specified.
Same root cause?

Yes, same root cause.

More examples of bug: https://github.com/radioegor146/native-obfuscator
This bug makes test log unreadable at all!

is this issue resolved? I am having the same issue in IntelliJ 2019.2
Gradle version: 5.2.1
JUnit version: 5.5.2

Unfortunately @zohabylc

As much as it pains me to say it, I'm afraid there are no short-term plans to add support for nesting.

-- #5975 (comment)

I'd too love to see this resolved as our CI test results become more and more obscure as we progress with migration to JUNIT5.

commented

Any workaround for this?

Hi Boys,
Any progression for this sujet?

Just upgraded to

------------------------------------------------------------
Gradle 6.0-20191007230021+0000
------------------------------------------------------------

Build time:   2019-10-07 23:00:21 UTC
Revision:     fe3c9377c4597e7458cf3965d5ef5ebf8dce57df

Kotlin:       1.3.50
Groovy:       2.5.8
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          13 (Oracle Corporation 13+33)
OS:           Linux 5.2.17-200.fc30.x86_64 amd64

and am still seeing

xyz.leutgeb.lorenz.logs.ParserTest > snippets(String, Type)[1] PASSED
xyz.leutgeb.lorenz.logs.ParserTest > snippets(String, Type)[2] PASSED
xyz.leutgeb.lorenz.logs.ParserTest > snippets(String, Type)[3] PASSED
xyz.leutgeb.lorenz.logs.ParserTest > snippets(String, Type)[4] PASSED

instead of names. These are ParameterizedTests.

AFAIK there's not been any progress. FWIW if you're only concerned about build output, here's a workaround for that: junit-team/junit5#2041 (comment)

I created #10983 to address the logging portion of this issue.

commented

I had the same problem and I'm using gradle 5.5 and JUnit 5. But after I typed in the console of gradle: "gradle build" and then run the test with Intelij IDEA it showed the correct names (DisplayName). Hopefully this information will help you.

Maybe it is an ugly workaround but still if you do want that display name will show you can change in source code:
https://github.com/junit-team/junit5/blob/master/junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestIdentifier.java

The line

String legacyReportingName = testDescriptor.getLegacyReportingName();

to line

String legacyReportingName = displayName;

Then you will get @DisplayName working

This is really annoying, I need to open gradle html report to see proper names, instead of just checking intellij test runner tab.

I solved it by unchecking the gradle extension plugin

As a workaround, I set Run tests using: IntelliJ IDEA in the IDEA Build, Execution, Deployment > Build Tools > Gradle preferences.

Screen Shot 2020-02-21 at 4 45 47 PM

commented

As a workaround, I set Run tests using: IntelliJ IDEA in the IDEA Build, Execution, Deployment > Build Tools > Gradle preferences.

Screen Shot 2020-02-21 at 4 45 47 PM

Nice Workaround!!!
Do you also have workaround for Jenkins?

Nice Workaround!!!
Do you also have workaround for Jenkins?

I don't, sorry. If I needed one I might try to cobble something together using ConsoleLauncher.

Current status of JUnit 5 output as of #10983 's completion which went into gradle 6.1:

  • pre-6.1 gradle console output doesn't show custom display names for either regular or param tests.

  • pre-6.1 gradle console output doesn't show param test cases' data in gradle console output (but shows the method name and the case index).

  • 6.1-onward gradle console output shows custom display names for regular test methods, but parameterized tests now ONLY show the param cases, and show neither the param method's name nor display name.
    E.g.:

ExampleUnitTest > [1] null FAILED
    org.opentest4j.AssertionFailedError: expected: not <null>
        at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:39)
        at org.junit.jupiter.api.Assertions.fail(Assertions.java:109)
        at org.junit.jupiter.api.AssertNotNull.failNull(AssertNotNull.java:47)
        at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:36)
        at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:31)
        at org.junit.jupiter.api.Assertions.assertNotNull(Assertions.java:283)
        at ExampleGradleProject.ExampleUnitTest.param(ExampleUnitTest.java:38)

ExampleUnitTest > [2]  PASSED

ExampleUnitTest > [3] abc PASSED

ExampleUnitTest > [4] 123 PASSED

From playing around with testLogging.afterTest, the limitation appears to be that parameterized test index/data (e.g. "[3] abc") is put in the test's display name field, overriding any display name set by @DisplayName for param tests. I also can't find any foolproof way to distinguish a parameterized test case from a regular test given just TestDescriptor/TestResult, which seems potentially problematic too.

A naive solution I can think of would be to give TestDescriptor a parameterData (or case, etc.) field, which is null/empty for non-parameterized tests. One could then simply always output displayName, and also output parameterData if available.

Gradle should show the names for intermediate elements in the test tree as well. I the meantime, the name that's displayed above can be customized using @ParameterizedTest(name = "...").

Current status of JUnit 5 output as of #10983 's completion which went into gradle 6.1:

  • pre-6.1 gradle console output doesn't show custom display names for either regular or param tests.
  • pre-6.1 gradle console output doesn't show param test cases' data in gradle console output (but shows the method name and the case index).
  • 6.1-onward gradle console output shows custom display names for regular test methods, but parameterized tests now ONLY show the param cases, and show neither the param method's name nor display name.
    E.g.:
ExampleUnitTest > [1] null FAILED
    org.opentest4j.AssertionFailedError: expected: not <null>
        at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:39)
        at org.junit.jupiter.api.Assertions.fail(Assertions.java:109)
        at org.junit.jupiter.api.AssertNotNull.failNull(AssertNotNull.java:47)
        at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:36)
        at org.junit.jupiter.api.AssertNotNull.assertNotNull(AssertNotNull.java:31)
        at org.junit.jupiter.api.Assertions.assertNotNull(Assertions.java:283)
        at ExampleGradleProject.ExampleUnitTest.param(ExampleUnitTest.java:38)

ExampleUnitTest > [2]  PASSED

ExampleUnitTest > [3] abc PASSED

ExampleUnitTest > [4] 123 PASSED

I used gradle 6.2.2 and still the tests dont show the display name in intellij. But shows in Gradle report.

UPDATE
Its working in Log, but not in UI. maybe intellij bug
image

Yes, that's correct. For IntelliJ to show it, it needs to be exposed in the Gradle's Tooling API.

This problem is worse with frameworks like https://github.com/dmcg/minutest which depend entirely on the display names to provide useful information:

minutest-delegate

I tested with a JUnit 5 nested test and appears that the hierarchy is at least reflected:

IntelliJ Runner

Gradle Runner

This has become a more frequent ask since IntelliJ 2020.1 switched to delegating to Gradle by default, so would be great for this to get some attention.

Using @ParameterizedTest and @MethodSource on Gradle 6.5.1 and JUnit 5.5.1. Same problem; @DisplayName has no effect (see this test case).

What I see in the report are names based on my method arguments, which are super long strings. This is just a portion of the screenshot (in reality, the test names expand so much towards the right you can sit and scroll forever):

Capture

There's two problems with this: 1) The generated report becomes so large it's practically impossible to open it in a web browser. 2) Given the vast expansion of the generated report size, this puts a limit on how many repetitions/parameters I can use without fear that report generation will impact negatively the performance and perhaps even the verified test results.

@martinanderssondotcom That looks like it is the display name of the parameterized test invocation. The @DisplayName on the @ParameterizedTest method is not used by Gradle since its an intermediate container that is currently ignored by Gradle entirely.

@marcphilipp I am really sorry but I don't understand. I do understand you're saying @DisplayName is "ignored" by Gradle but, then maybe it's time to make Gradle not ignore it. Also, see earlier comments in this thread; making the display name work is sort of the whole idea of this reported issue, no?

The tree looks like this (in JUnit Platform):

- TestClass
  - method (with `@DisplayName`)
    - invocation (name from `@ParameterizedTest` annotation)

@DisplayName is not ignored by Gradle but the intermediate container "method" is.

So what your saying is this particular combo (@DisplayName + @ParameterizedTest) with JUnit specifically and not Gradle?

The screenshot you've posted above is showing display names which is a general JUnit Platform concept. @DisplayName is just one way of defining a display name which is used by Gradle if the method is a regular test method. In your case, it looks like a @ParameterizedTest method, though.

It does this with dynamictest... and that's not the expected behavior... displayname are displayed correctly running in eclipse or idea... it should be the same with gradle reports.

I tried another combo, this:

@DisplayName("this still has no effect")
@ParameterizedTest(name = "this becomes the \"test\" name")

Produced this:

Capture

So, I am happy that setting the name attribute of @ParameterizedTest solved my particular problem of extremely long generated names, but @DisplayName still has no effect and it is supposed to have some kind of an effect, even when combined with @ParameterizedTest. This much I know because the JUnit 5 user guide has an example with this combo. Surely they wouldn't write this example if one of these annotations are supposed to exclude or make the other one ignored?

Given the screenshot I just gave, there's two columns and there's two ways to specify a name, so I suspect that they should each go to a column of their own and not exclude each other. The million dollar question is which one should go where.

JUnit 5 docs is saying @DisplayName is the name of the "test method" and @ParameterizedTest(name = ...) is the name to use for "individual invocations".

But, this still doesn't answer the question definitively because Gradle itself (or rather, the report generator whoever that is) seems to be confused what the columns are and changes their semantics depending on "test type".

When running a non-parameterized test I have one column only called "test", default value of which is the declared method name in my source code file and I am guessing the value of this would be changed by @DisplayName.

When running a parameterized test I get two columns; "test" and "method name". Now, "test" is no longer the declared method name as it used to be. The declared method name is sort of moved into the "method name" column which is the method named combined with another generated part and this can not be changed with @DisplayName which simply cease to have an effect. The fully generated name however now becomes the "test" column which we can change with @ParameterizedTest(name ...).

So, very confusing! My vote is to exactly define these columns and absolutely not change their semantics no matter what type of test we run. We have column "Test" always in the report for all test types, the value of which is the static test name; declared method name possibly changed by @DisplayName. But, parameterized tests adds a second column "Invocation" which is the generated name, affected by @ParameterizedTest(name ...). Or something similar. See my point?

I believe the issue you're describing is #4438.

following, am I to take some of the comments in here to say that gradle test (if I run it on the cli?) should be able to print the hierarchy in some way already? if so how would that be achieved

Is there any milestone for this issue to be fixed?

following, am I to take some of the comments in here to say that gradle test (if I run it on the cli?) should be able to print the hierarchy in some way already?

No, that's currently not supported.

Is there any milestone for this issue to be fixed?

I'll bring it up with @gradle/jvm.

for me i was using both ju4 and ju5 in pom.xl
delete ju4 dependency solved my problem

I'm glad to see that this issue has been self-assigned! But if for whatever reason it's put on hold, then I'd be more than happy to give it a shot as an external contributor.

Just ran across this problem, I am happy to see that is looks like it is being addressed.

Same problem here with tests generated by @TestFactory.

commented

2 years later, gradle 7 available, and still have this issue... can't we fix it please?

What about a "Gradle professional" edition beside of the "Gradle Enterprise" product, where customers pay for a product for which annoying issues like this would be resolved within a reasonable amount of time.

The issue is "fixable" in theory, isn't it?
And it's not rocket science, isn't it?
Isn't it just a matter of priority and probably too many things concurrently going on?

With an open source tool like Gradle, issues like this are acceptable.
You don't have to use it, after all.
But with a professional product, it wouldn't be acceptable.

IMHO 😅

This issue is quite old and most of the problems with dynamic names have been fixed already. BUT:

  • if it's not the case for you, please make sure to provide us with a reproducer on latest Gradle
  • please make sure to include the IntelliJ version you are using, since there are fixes to be done on IDEA too for which we can't do anything except pinging them

And yes, we're very happy to get help on this.

This is still broken in 7.0.2, it was working in all versions of 6.x
I'm happy to help with this if someone nudge in roughly the right direction (running JUnit tests in Gradle)

Same problem with Gradle 7.3 + JUnit 5.8.2 :/

I'm also experiencing this issue. Gradle 7.3; JUnit 5.8.2 (same as @maio) via Linux Bash shell (no IDE).

Gradle 7.1.1 and JUnit 5.7.2 seems to be no problem

Gradle 7.1.1 and JUnit 5.7.2 seems to be no problem

Can you share a toy project with both of them working? I definitely cannot see it working with 7.1.1 and 5.7.2

My dynamic tests are all been printed as TestXY[0][2] instead of the dynamically generated display name.

Again, to help the Gradle team it's be better for them to do what I referred to in my comment: if you report something broken, you must include both the IntelliJ and Gradle versions, since there are fixes which can't be done on the Gradle side.

Gradle 7.1.1 and JUnit 5.7.2 seems to be no problem

Can you share a toy project with both of them working? I definitely cannot see it working with 7.1.1 and 5.7.2

My dynamic tests are all been printed as TestXY[0][2] instead of the dynamically generated display name.

I'm sorry for my wrong opinion. I was using JUnit plugin in IntelliJ ide instead of gradle test command to run dynamic tests so it seems to be no problem.

Is there any update on this issue. Its still broken for me. I am using gradle 6.8 and junit 5.3.2

Here's a reproducer of this problem:
https://github.com/jlleitschuh/ktlint-gradle

Run the task ./gradlew :plugin:test --scan and observe the test names in the scan output. They will not use the display names.

Here's an example build scan:
https://scans.gradle.com/s/t6dhlgynjc23y

Gradle 7.1.1, Gradle Enterprise plugin 3.6

I've created a PR to fix this issue:
#21361

It's my first PR on Gradle so please me let me know if I need to change something

why isn't this fixed/merged yet? super obnoxious

I'm experiencing the problem from within Android Studio, since you can run JUnit tests through gradle in the IDE now.

Glad to see there's been activity on the PR, though!

Whoever is chasing the dragons tail and arrived here because of this: https://youtrack.jetbrains.com/issue/IDEA-234232/JUnit-5-display-name-ignored-when-running-tests-in-a-Gradle-project

This:

tasks.withType<Test> {
    useJUnitPlatform()
    afterTest(
        KotlinClosure2({ descriptor: TestDescriptor, result: TestResult ->
            println("\n$descriptor.className [$descriptor.classDisplayName] > $descriptor.name [$descriptor.displayName]: $result.resultType")
        })
    )
}

DOES NOT WORK.

This will be fixed in Gradle 8.1

I have an issue where this test code

    @ParameterizedTest
    @DisplayName("parameterizedTest")
    @ValueSource(strings = {"a", "b", "c"})
    void parameterizedTest(String testValue) {
        assertEquals(testValue, testValue);
    }

will produce this output

  <testcase name="[1] a" classname="de.tomtec.ci.teamcity.XmlReportingTest" time="0.011"/>
  <testcase name="[2] b" classname="de.tomtec.ci.teamcity.XmlReportingTest" time="0.001"/>
  <testcase name="[3] c" classname="de.tomtec.ci.teamcity.XmlReportingTest" time="0.001"/>

My Gradle version is this: Gradle 7.6.2

@ljacomet Can you tell me if this will be fixed in Gradle 8.1, too? Or should I open another bug?

@Franziskus1988 Currently, the latest Gradle version is 8.7. Update your project to that and try running your test again!

@mannodermaus Tested with Gradle 8.7 and the issue persists. Guess I will create a new issue.