From Eclipse I can easily run all the JUnit tests in my application.
I would like to be able to run the tests on target systems from the application jar, without Eclipse (or Ant or Maven or any other development tool).
I can see how to run a specific test or suite from the command line.
I could manually create a suite listing all the tests in my application, but that seems error prone - I'm sure at some point I'll create a test and forget to add it to the suite.
The Eclipse JUnit plugin has a wizard to create a test suite, but for some reason it doesn't "see" my test classes. It may be looking for JUnit 3 tests, not JUnit 4 annotated tests.
I could write a tool that would automatically create the suite by scanning the source files.
Or I could write code so the application would scan it's own jar file for tests (either by naming convention or by looking for the @Test annotation).
It seems like there should be an easier way. What am I missing?
According to a recent thread on the JUnit mailing list, ClasspathSuite can collect and run all JUnit tests on the classpath. It is not precisely what you want, since it is a class-level annotation, but the source is available, so you may be able to extend its internal discovery mechanism.
I ran into a minor problem with my last solution. If I ran "all tests" from Eclipse they ran twice because they ran the individual tests AND the suite. I could have worked around that, but then I realized there was a simpler solution:
package suneido;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class RunAllTests {
public static void run(String jarfile) {
String[] tests = findTests(jarfile);
org.junit.runner.JUnitCore.main(tests);
}
private static String[] findTests(String jarfile) {
ArrayList<String> tests = new ArrayList<String>();
try {
JarFile jf = new JarFile(jarfile);
for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) {
String name = e.nextElement().getName();
if (name.startsWith("suneido/") && name.endsWith("Test.class")
&& !name.contains("$"))
tests.add(name.replaceAll("/", ".")
.substring(0, name.length() - 6));
}
jf.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
return tests.toArray(new String[0]);
}
public static void main(String[] args) {
run("jsuneido.jar");
}
}
Based on http://burtbeckwith.com/blog/?p=52 I came up with the following. It seems to work well.
I can run it from within my code with:
org.junit.runner.JUnitCore.main("suneido.AllTestsSuite");
One weak point is that it relies on a naming convention ("Test" suffix) to identify tests. Another weak point is that the name of the jar file is hard coded.
package suneido;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.model.InitializationError;
/**
* Discovers all JUnit tests in a jar file and runs them in a suite.
*/
@RunWith(AllTestsSuite.AllTestsRunner.class)
public final class AllTestsSuite {
private final static String JARFILE = "jsuneido.jar";
private AllTestsSuite() {
}
public static class AllTestsRunner extends Suite {
public AllTestsRunner(final Class<?> clazz) throws InitializationError {
super(clazz, findClasses());
}
private static Class<?>[] findClasses() {
List<String> classFiles = new ArrayList<String>();
findClasses(classFiles);
List<Class<?>> classes = convertToClasses(classFiles);
return classes.toArray(new Class[classes.size()]);
}
private static void findClasses(final List<String> classFiles) {
JarFile jf;
try {
jf = new JarFile(JARFILE);
for (Enumeration<JarEntry> e = jf.entries(); e.hasMoreElements();) {
String name = e.nextElement().getName();
if (name.startsWith("suneido/") && name.endsWith("Test.class")
&& !name.contains("$"))
classFiles.add(name.replaceAll("/", ".")
.substring(0, name.length() - 6));
}
jf.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static List<Class<?>> convertToClasses(
final List<String> classFiles) {
List<Class<?>> classes = new ArrayList<Class<?>>();
for (String name : classFiles) {
Class<?> c;
try {
c = Class.forName(name);
}
catch (ClassNotFoundException e) {
throw new AssertionError(e);
}
if (!Modifier.isAbstract(c.getModifiers())) {
classes.add(c);
}
}
return classes;
}
}
}
I have not tried this as of yet, but came across this blog recently: http://burtbeckwith.com/blog/?p=52
The author provides a class that discovers all your junits and runs them, so if you slot this in to your project it may provide the capability required?
Hope this helps.
Get the Java project and pass the project
JUnitLaunchShortcut jUnitLaunchShortcut = new JUnitLaunchShortcut();
jUnitLaunchShortcut.launch("Pass the Java Project containing JUnits Classes", "run");
You also could use ANT which has built-in task.
Write ANT script and run it on target machine.
ANT could create report as result.