Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I include test classes into Maven jar and execute them?

In a Maven project, I have test classes and source classes in the same package, but in different physical locations.

.../src/main/java/package/** <-- application code .../src/test/java/package/** <-- test code 

It's no problem to access the source classes in the test classes, but I would like to run a test runner in the main method and access the AllTest.class so that I can create jar and execute my tests.

 public static void main(String[] args) {     // AllTest not found     Result result = JUnitCore.runClasses(AllTest.class);     for (Failure failure : result.getFailures()) {         System.out.println(failure.toString());     }     System.out.println(result.wasSuccessful()); } 

But it doesn't work as I don't have access to the test code. I don't understand since they are in the same package.

Question: how can access test classes from application classes? Alternatively, how can Maven package a fat jar including test classes and execute tests?

like image 246
jam Avatar asked Mar 16 '16 21:03

jam


People also ask

Are test classes included in jar?

You can produce a jar which will include your test classes and resources. To reuse this artifact in an other project, you must declare this dependency with type test-jar : <project> ...

Which command can be used to run the test classes in a maven project?

The Maven surefire plugin provides a test parameter that we can use to specify test classes or methods we want to execute. If we want to execute a single test class, we can execute the command mvn test -Dtest=”TestClassName”.


1 Answers

You should not access test classes from your application code, but rather create a main (the same main) in the test scope and create an additional artifact for your project.

However, in this additional artifact (jar) you would need to have:

  • The test classes
  • The application code classes
  • External dependencies required by application code (in compile scope)
  • External dependencies required by the test code (in test scope)

Which basically means a fat jar with the addition of test classes (and their dependencies). The Maven Jar Plugin and its test-jar goal would not suit this need. The Maven Shade Plugin and its shadeTestJar option would not help neither.

So, how to create in Maven a fat jar with test classes and external dependencies?

The Maven Assembly Plugin is a perfect candidate in this case.

Here is a minimal POM sample:

<project>     <modelVersion>4.0.0</modelVersion>     <groupId>com.sample</groupId>     <artifactId>sample-project</artifactId>     <version>1.0-SNAPSHOT</version>      <build>         <plugins>             <plugin>                 <artifactId>maven-assembly-plugin</artifactId>                 <version>2.3</version>                 <configuration>                     <descriptor>src/main/assembly/assembly.xml</descriptor>                 </configuration>                 <executions>                     <execution>                         <id>make-assembly</id>                         <phase>package</phase>                         <goals>                             <goal>single</goal>                         </goals>                         <configuration>                             <archive>                                 <manifest>                                     <mainClass>com.sample.TestMain</mainClass>                                 </manifest>                             </archive>                         </configuration>                     </execution>                 </executions>             </plugin>         </plugins>     </build>      <dependencies>         <dependency>             <groupId>junit</groupId>             <artifactId>junit</artifactId>             <version>4.11</version>             <scope>test</scope>         </dependency>     </dependencies> </project> 

The configuration above is setting the main class defined by you in your test classes. But that's not enough.

You also need to create a descriptor file, in the src\main\assembly folder an assembly.xml file with the following content:

<assembly     xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">     <id>fat-tests</id>     <formats>         <format>jar</format>     </formats>     <includeBaseDirectory>false</includeBaseDirectory>     <dependencySets>         <dependencySet>             <outputDirectory>/</outputDirectory>             <useProjectArtifact>true</useProjectArtifact>             <unpack>true</unpack>             <scope>test</scope>         </dependencySet>     </dependencySets>     <fileSets>         <fileSet>             <directory>${project.build.directory}/test-classes</directory>             <outputDirectory>/</outputDirectory>             <includes>                 <include>**/*.class</include>             </includes>             <useDefaultExcludes>true</useDefaultExcludes>         </fileSet>     </fileSets> </assembly> 

The configuration above is:

  • setting external dependencies to be taken from the test scope (which will also take the compile scope as well)
  • setting a fileset to include compiled test classes as part of the packaged fat jar
  • setting a final jar with fat-tests classifier (hence your final file will be something like sampleproject-1.0-SNAPSHOT-fat-tests.jar).

You can then invoke the main as following (from the target folder):

java -jar sampleproject-1.0-SNAPSHOT-fat-tests.jar 

From such a main, you could also invoke all of your test cases as following:

  • Create a JUni test suite
  • Add to the test suite the concerned tests
  • Invoke the test suite from your plain Java main

Example of test suite:

import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses;  @RunWith(Suite.class) @SuiteClasses({ AppTest.class }) public class AllTests {  } 

Note: in this case the test suite is only concerning the AppTest sample test.

Then you could have a main class as following:

import org.junit.internal.TextListener; import org.junit.runner.JUnitCore;  public class MainAppTest {      public static void main(String[] args) {         System.out.println("Running tests!");          JUnitCore engine = new JUnitCore();         engine.addListener(new TextListener(System.out)); // required to print reports         engine.run(AllTests.class);     } } 

The main above would then execute the test suite which will in chain execute all of the configured tests.

like image 136
A_Di-Matteo Avatar answered Sep 27 '22 20:09

A_Di-Matteo