Junit5: Tests not found via classpath scanning within nested JAR in Spring Boot JAR

Created on 10 Jan 2019  路  17Comments  路  Source: junit-team/junit5

We are providing a test suite for our applications as Spring Boot application. The test suite is basically a set of regular JUnit 5 tests that are launched in the Spring Boot application.
The application is packaged as Spring Boot JAR (created with the spring-boot-maven-plugin).

We do now have the problem, that the tests are not found when we are using DiscoverySelectors.selectPackage() and run the application as Spring Boot JAR. Everything works fine, when we select the tests by classname. Executing the application within an IDE works fine as well.

The problem seems to be in ClasspathScanner#findClassesForPath()). Files.walkFileTree() does not work with nested JAR files. A similar problem was already reported in #399.

Steps to reproduce

I created a project on GitHub to reproduce this issue. When the Application is run within the IDE, everything works fine. When it is executed as Spring Boot JAR, the test is not found.

Context

  • Used versions (Jupiter/Vintage/Platform): junit-bom 5.3.2
  • Build Tool/IDE: Maven, OpenJDK11, IntelliJ

    • Spring Boot: 2.1.1.RELEASE

Platform waiting-for-interest discovery enhancement

Most helpful comment

Yes, this problem is relevant for other users. Please fix it.

All 17 comments

Thanks for raising the issue.

I've tentatively slated this for 5.4 M2 for the purpose of _team discussion_.

Today I stumbled over the same problems. Thanks for creating a demo project @ferstl!

I'll a) push an integration test showing the issue is even applicable when test classes are jarred (not nested) and b) tackle the underlying missing feature/enhancement after 5.5 GA is released.

Team Decision: Move to General Backlog to see whether this use case is relevant for other users as well.

Yes, this problem is relevant for other users. Please fix it.

If this issue is ever addressed, I wonder if it would be worth including ClassGraph, shadowing it, and using it instead of ClasspathScanner. 馃

Interesting idea, @jbduncan!

I didn't realize that ClassGraph had explicit support for Spring Boot's proprietary JAR structure until now.

Jarfiles within jarfiles (to unlimited nesting depth), e.g.
project.jar!/BOOT-INF/lib/dependency.jar, as required by Spring-Boot, JBoss, and Felix classloaders, and probably others.

I've spiked implementing ClasspathScanner using ClassGraph and it passes all the tests:
https://github.com/junit-team/junit5/commit/9000a80327d92820e095e5b579c58d97efbd8de0

The build is currently broken due to some error in our module compilation setup:
https://scans.gradle.com/s/z2kzsnwxcrt7y/console-log?task=:junit-jupiter:compileModule

@sormuras Let's take a look at that when you're back.

Why so late? :nerd_face:

Looks like the ClassGraph JAR is not available when compiling the modules. Neither on the module-path, nor on the class-path. Will look into it ... soon.

Hello
I have faced with this issue triying to run my tests from spring boot jar.
I use gradle bootJar task and place my tests to BOOT-INF/classes as spring using this

    from(sourceSets.test.output) {
        into 'BOOT-INF/classes'
    }

I can see that the problem is in CloseablePath.create(baseUri) method where you split the path by "!" symbol. It returns "BOOT-INF/classes" instead of "BOOT-INF/classes/com.target.package.name"

The reason is that full path in spring boot jar contains two "!" signs like this
"jar:file:/C:/Users/IdeaProjects/build/libs/application.jar!/BOOT-INF/classes!/com/target/package/name"

I decided to use -c and pass full class name and it works. But won't be handy if I have a lot of tests

@epam-valerii is correct, the baseDir is incorrect so the subPackage variable in determineSubpackageName is returning the full package instead of '' like it supposed to. @marcphilipp any update on this issue? It still has not been fixed in 5.5.2.

The team decision from above to wait for additional interest still stands.

I've found a workaround.
Gradle Shadow plugin could be used instead of bootJar task for not to produce nested Jars.

https://github.com/spring-projects/spring-boot/issues/1828#issuecomment-231104288

With configuration below Spring Boot features work alongside with JUnit Console Launcher --select-package option

import com.github.jengelman.gradle.plugins.shadow.transformers.*
shadowJar {
    zip64 true

    manifest {
        attributes 'Implementation-Title': 'Testing Jar File',
                'Main-Class': 'org.junit.platform.console.ConsoleLauncher'
    }
    from sourceSets.test.output
    configurations = [project.configurations.testRuntimeClasspath]
    exclude '**/Log4j2Plugins.dat'

    // Required for Spring
    mergeServiceFiles()
    append 'META-INF/spring.handlers'
    append 'META-INF/spring.schemas'
    append 'META-INF/spring.tooling'
    transform(PropertiesFileTransformer) {
        paths = ['META-INF/spring.factories' ]
        mergeStrategy = "append"
    }
}

Same issue here, currently declaring every class via LauncherDiscoveryRequestBuilder

Hit this issue for integration tests run using JUnit 5 that are packaged up for a CI/CD pipeline as a Spring Boot application.

Just chiming in here. We ran into this issue as well. Our motivation for this is to run E2E-tests within a cluster by deploying a Spring Boot Application executing the tests using the JUnit5 platform launcher.

Same issue here. Any update on this issue?

No, but someone could pick up where I left off above: https://github.com/junit-team/junit5/issues/1724#issuecomment-518027950 馃檪

Was this page helpful?
0 / 5 - 0 ratings