How to Include Test Classes into Maven Jar and Execute Them

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

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 JUnit 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.

How to include test classes in Jar created by maven-shade-plugin?

I've solved my problem a different way; using two other plugins.

First I use the maven-dependency-plugin to get the dependencies and unpack them locally, then I use the maven-jar-plugin to create a test-jar and include the classes from the unpacked dependencies.

How to package test classes into the jar without running them?

if youre following maven conventions then your test classes are under src/test/java.
maven never packages the contents of the test sources subdirectory into the artifact.

you have (at least ...) 3 alternativs:

put the tests with the "normal" sources

if you REALLY want them packaged (why?) then you should place the test classes under src/main/java where they will be treated as normal source and their compiled classes packaged into the artifact (usually *.jar)

you imght run into all sorts of issues doing this. for example your artifact will have a compile-scoped dependency on junit, which might interfere with other modules using your jar.

also, you might have to configure the maven plugins running the tests to be aware of your test classes if you do this (that is if you ever do want to run tests as part of your build). for example the surefire and failsafe plugins.

configure the maven jar plugin to build a tests jar

see the maven jar plugin documentation. they even have a section titled "How to create a jar containing test classes" - the instructions there will cause your tests to be packaged into a separate jar file (which is probably a much better idea).

create the jar your way using the assembly plugin directly

yuo could disable the default execution of the jar plugin and instead add an execution of the assembly plugin to the package phase to create a jar. you will need to write an assembly descriptor for this (not as complicated as the above link makes it out to be). this will give yu full control over what get included in the jar produced. its best to start by copying one of the predefined assemblies

maven include main project into test-jar

You can't add other classes to the test-jar trough the maven-jar-plugin but you can do a jar that contains main project classes and test classes in another way: using the maven-assembly-plugin!

<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<descriptor>src/assembly/dep.xml</descriptor>
</configuration>
<executions>
<execution>
<id>create-archive</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>

This will create an artifact with the information described in the assembly descriptor, the descriptor default path is src/assembly/.xml
In the descriptor you want to add this:

<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly- plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>example</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.build.testOutputDirectory}</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
<fileSet>
<directory>${project.build.outputDirectory}</directory>
<outputDirectory>/</outputDirectory>
</fileSet>
</fileSets>
</assembly>
  • id -> postfix to the jar
  • format -> format of the output file (we're creating a jar)
  • fileSets -> all the directories to add
  • fileSet -> single directory to add, with outputDirectory, include exclude ecc.

this example would create a jar with -example as the postfix (ex: plugin-1.0-example.jar) with both main and test files

How do I include a dependency's test jar into a Maven project's deployment?

Better to configure maven pom file in your first module

<project>
<groupId>com.example</groupId>
<artifactId>foo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>

After this a mvn install/release will also deploy an artifact foo-1.0.0-SNAPSHOT-tests.jar

Then configure the dependency on the test jar with classifier (like suggested in other responses)

<dependency>
<groupId>com.example</groupId>
<artifactId>foo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>test-jar</type>
<!-- uncomment if needed in test scope only
<scope>test</scope>
-->
</dependency>


Related Topics



Leave a reply



Submit