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?
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> ...
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”.
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:
compile
scope)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:
test
scope (which will also take the compile
scope as well)fileset
to include compiled test classes as part of the packaged fat jarfat-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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With