javacommand-lineclasspathjunit5

Inconsistency when running java jar via CLI and IntelliJ


My project is in the following structure:

src
└── main
    └── java
        └── tests
            ├── functional
            │   ├── TestA
            │   └── TestB
            ├── verification
            │   └── TestC
            └── TestsRunner.java
target
├── classes
├── lib
│   ├── depA
│   ├── depB
│   └── ..
├── app.jar
└── ..

The main class, is at the TestsRunner.java class -

package tests;

import lombok.extern.slf4j.Slf4j;
import picocli.CommandLine;

import java.util.concurrent.Callable;


@Slf4j
@CommandLine.Command(name = "run", mixinStandardHelpOptions = true, version = "1.0", description = "Run tests")
public class TestsRunner implements Callable<Integer> {

  @CommandLine.Option(names = {"-s", "--suite"}, required = true, description = "Suite name")
  private String testSuiteName;

  @CommandLine.Option(names = {"-o", "--output"}, required = true, description = "Test reports output directory")
  private String reportOutputDir;

  public static void main(String[] args) {
    int exitCode = new CommandLine(new TestsRunner()).execute(args);
    System.exit(exitCode);
  }

  @Override
  public Integer call() throws Exception {
    var basePackage = switch (testSuiteName) {
      case "functional" -> "tests.functional";
      case "verification" -> "tests.verification";
      default -> throw new RuntimeException("Unknown tests suite: " + testSuiteName);
    };
    var runner = Junit5ServiceTestsRunner.builder()
      .classNamePattern(".*Test")
      .classNamePattern(".*Tests")
      .basePackage(basePackage)
      .reportOutputDir(reportOutputDir)
      .build();
    var result = runner.run();
    return result.isSuccess() ? 0 : 1;
  }
}

I am also using the maven-jar-plugin plugin in order to copy all dependencies to lib directory and state the main class

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <version>3.2.0</version>
    <configuration>
        <finalName>app</finalName>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <Main-Class>tests.TestsRunner</Main-Class>
                <classpathPrefix>lib</classpathPrefix>
                <!--workaround for https://issues.apache.org/jira/browse/MJAR-156-->
                <!--https://stackoverflow.com/questions/67505356/maven-jar-plugin-generates-jar-with-wrong-manifest-mf-->
                <useUniqueVersions>false</useUniqueVersions>
            </manifest>
            <manifestEntries>
                <Class-Path>.</Class-Path>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

When running the TestsRunner class via IntelliJ, I use the Modify run configurations to provide the -s and -o values. Running the application via IntelliJ works, the output contains the results of the tests that were executed (notice the 1 tests successful):

Test run finished after 4246 ms
[         3 containers found      ]
[         0 containers skipped    ]
[         3 containers started    ]
[         0 containers aborted    ]
[         3 containers successful ]
[         0 containers failed     ]
[         1 tests found           ]
[         0 tests skipped         ]
[         1 tests started         ]
[         0 tests aborted         ]
[         1 tests successful      ]
[         0 tests failed          ]

Now, I see that IntelliJ uses their own java agent, and it also states the classpath explicitly on each run:

"C:\Program Files\Java\jdk-17.0.8\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1.3\lib\idea_rt.jar=65474:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2022.1.3\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\xxx\Documents\GitHub\my-project\tests\target\classes;C:\Users\xxx\.m2\repository\info\picocli\picocli\4.7.1\picocli-4.7.1.jar;C:\Users\xxx\.m2\repository\com\core\testing\mock-servers-junit5\1.1.0\mock-servers-junit5-1.1.0.jar ..........(all other dependencies) tests.TestsRunner -s verification -o report

When running from the command line at the target directory using:

java -jar app.jar -o ${JUNIT_OUTPUT_FOLDER} -s ${SUITE_NAME}

It is as if it does not recognize or find any tests. I receive the following output:

Test run finished after 62 ms
[         1 containers found      ]
[         0 containers skipped    ]
[         1 containers started    ]
[         0 containers aborted    ]
[         1 containers successful ]
[         0 containers failed     ]
[         0 tests found           ]
[         0 tests skipped         ]
[         0 tests started         ]
[         0 tests aborted         ]
[         0 tests successful      ]
[         0 tests failed          ]

Probably worth mentioning that the testing framework is JUnit5, the tests are triggered using a template and an extension.

@TestTemplate
@ExtendWith(SomeClass.class)

There are no other functions that are annotated with @Test.

The thing is, my projects are generated from a template. All of my service are structured and behave the same. This is the only service that I ran into this issue.

I thought this has something to do with the classpath, but I see in my app.jar that it is ok, manifest looks fine, it has all the dependencies, main class is correct, and test classes are present. But still, I have tried changing java jar to java cp and state the directories of the classpath, and I passed the main class as well -

java -cp "app.jar:lib/*:classes" tests.TestsRunner -s verification -o reports

But still the same result.

Any suggestion will be great, thanks.


Solution

  • I have added the maven-shade-plugin plugin, removed the part where I copy all dependencies to lib directory and had it all packaged together, that did the trick.