A few months ago we had a problem where Eclipse could not automatically run all jUnit unit tests in a package if that package references a class called “enum”, which is a reserved word in Java 1.6. I’ll spare you the details, but we were forced to create a TestSuite. Normally we avoid this construction because it’s easy to create a new unit test and forget to add it to the correct TestSuit. So as a workaround we wrote some code which could build and return a TestSuite dynamically. Right-click in eclipse, select “Run as Unittest”, sit back and enjoy.
Lately this piece of code came in handy while testing another application, which required the removal of data from a database. Yes I know, Unittests should maybe not depend on databases because it leans towards integration testing, but here we are, and I need to solve it. I used the old TestSuite code and changed it so that the TestCase I needed to run first was singled out, while still maintaining the functionality of auto-detecting testcases in the source folder.
I’ve cleaned up the code and made an example implementation which sorts the test cases in alpabetical order. It’s a simple starting point and definetely not the prettiest code I’ve ever written but it works, it helped me and it might help you. Copy/paste, and adjust to your own needs.
Have fun!
package com.rolfje.example;
import java.io.File;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import junit.framework.Test;
import junit.framework.TestSuite;
/**
* Constructs a testsuite by dynamically scanning for
* classes ending in "*Test", and allows for re-ordering of
* the Classes in the Suite.
*/
public class OrderedTestSuite {
public static Test suite() throws Exception {
// Find all test classes
List testClasses = findTestClasses();
// Custom test ordering example: Sort test classes in
// natural alphabetic order based on simple classname
Collections.sort(testClasses, new Comparator() {
@Override
public int compare(Class o1, Class o2) {
return o1.getSimpleName().compareTo(
o2.getSimpleName());
}
});
// Convert the Set to a TestSuite
TestSuite suite = new TestSuite(
"Custom ordered TestSuite");
for (Class testClass : testClasses) {
suite.addTestSuite(testClass);
}
return suite;
}
private static List findTestClasses()
throws Exception {
List testClasses = new ArrayList();
File testDir = getRootOfTestTree();
List files = new ArrayList();
getTestFiles(files, testDir);
int nameIdx = testDir.getAbsolutePath().length() + 1;
for (Iterator iterator = files.iterator(); iterator
.hasNext();) {
File file = (File) iterator.next();
String className = file.getAbsolutePath().substring(
nameIdx);
className = className
.replace(File.separatorChar, '.');
className = className.replaceAll(".class", "");
Class testClass = Class.forName(className);
// Prevent recursion
if (OrderedTestSuite.class.equals(testClass)) {
continue;
}
if (isTestClass(testClass)) {
testClasses.add(testClass);
}
}
return testClasses;
}
/**
*
* @param clazz Class to check
* @return <code>true</code> if the given Class is a
* usable implementation of {@link Test}
*/
private static boolean isTestClass(Class clazz)
throws Exception {
int modifiers = clazz.getModifiers();
if (Modifier.isAbstract(modifiers)
|| Modifier.isInterface(modifiers)
|| Modifier.isPrivate(modifiers)) {
return false;
}
return Test.class.isInstance(clazz.newInstance());
}
/**
* @return The root directory of the Java Test sources.
*/
private static File getRootOfTestTree() {
String meAsClasspathResource = OrderedTestSuite.class
.getResource(
OrderedTestSuite.class.getSimpleName()
+ ".class").getFile()
.replace('/', File.separatorChar);
String myLocation = OrderedTestSuite.class
.getCanonicalName();
myLocation = myLocation
.replace('.', File.separatorChar);
if (meAsClasspathResource == null
|| !meAsClasspathResource.contains(myLocation)) {
throw new RuntimeException(
"Can not find the class resource for "
+ OrderedTestSuite.class.getCanonicalName());
}
meAsClasspathResource = meAsClasspathResource
.substring(0,
meAsClasspathResource.indexOf(myLocation));
File dir = new File(meAsClasspathResource);
if (!dir.exists()) {
throw new RuntimeException("The directory "
+ dir.getAbsolutePath() + " does not exist.");
}
if (!dir.isDirectory()) {
throw new RuntimeException(dir.getAbsolutePath()
+ " is not a directory.");
}
return dir;
}
/**
* Recursively iterates through the nextFiles array to
* find all test files. which it subsequently returns.
*
* @param allTestFilesSoFar
* new Files will be added to this List
* @param nextDir
* The directory to scan for java Test files
*/
private static void getTestFiles(
List allTestFilesSoFar, File nextDir) {
File[] files = nextDir.listFiles();
for (int t = 0; t < files.length; t++) {
File nextFile = files[t];
if (nextFile.isDirectory()) {
getTestFiles(allTestFilesSoFar, nextFile);
} else if (nextFile.getName().endsWith("Test.class")
|| nextFile.getName().endsWith("Suite.class")) {
allTestFilesSoFar.add(nextFile);
}
}
}
}