Monday 29 September 2008

Write your own JUnit runner

I've been looking at how to integrate the JUnit test running model with a custom test framework.
This is a handy thing to have, because it allows your custom test framework to be used in all existing JUnit runners - and gives you IDE integration for free.

The RunWith annotation allows you to specify a custom runner for your class.
Extend the JUnit Runner class and over-ride the functions neccessary.



@RunWith(MyTargetTestClass.TheRunner.class)
public class MyTargetTestClass {
static int count = 0;
public boolean doStuff() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}

count ++;

if(count % 2 == 0) { throw new RuntimeException("A Failure"); }

return !(count % 3 == 0);
}

public static class TheRunner extends Runner {
List descriptions = new ArrayList();
private final Class<? extends MyTargetTestClass> testClass;
private final MyTargetTestClass testContainingInstance;
private Description testSuiteDescription;

public TheRunner(Class<? extends MyTargetTestClass> testClass) {
this.testClass = testClass;
testContainingInstance = reflectMeATestContainingInstance(testClass);
testSuiteDescription = Description.createSuiteDescription("All my stuff is happening now dudes");
testSuiteDescription.addChild(createTestDescription("first bit happening"));
testSuiteDescription.addChild(createTestDescription("second bit happening"));
testSuiteDescription.addChild(createTestDescription("third bit happening"));
}


@Override
public Description getDescription() {
return testSuiteDescription;
}

@Override
public void run(RunNotifier notifier) {
for(Description description : testSuiteDescription.getChildren()) {
notifier.fireTestStarted(description);
try {
if(testContainingInstance.doStuff()) {
notifier.fireTestFinished(description);
}
else {
notifier.fireTestIgnored(description);
}
} catch (Exception e) {
notifier.fireTestFailure(new Failure(description, e));
}
}

}

private MyTargetTestClass reflectMeATestContainingInstance(Class<? extends MyTargetTestClass> testClass) {
try {
return testClass.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
}

private Description createTestDescription(String description) {
return Description.createTestDescription(testClass, description);
}

}
}



It seems that the ability to write custom runners was added pretty late on, and as such the RunNotifier class has some methods that are indicated as being internal use only. Just make sure you don't call them and you'll be fine!

Blog Archive