Here is a basic implementation which seems to work (one annotates the test classes for which the test methods are to be repeated until success with @CustomJUnit4ClassRunner
instead of @SpringJUnit4ClassRunner
):
import org.junit.internal.runners.model.ReflectiveCallable;
import org.junit.internal.runners.statements.Fail;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
public class CustomJUnit4ClassRunner extends SpringJUnit4ClassRunner {
public CustomJUnit4ClassRunner(Class<?> clazz)
throws InitializationError {
super(clazz);
}
@Override
protected Statement methodBlock(FrameworkMethod frameworkMethod) {
Object testInstance;
try {
testInstance = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
}
catch (Throwable ex) {
return new Fail(ex);
}
Statement statement = methodInvoker(frameworkMethod, testInstance);
statement = possiblyExpectingExceptions(frameworkMethod, testInstance, statement);
statement = withBefores(frameworkMethod, testInstance, statement);
statement = withAfters(frameworkMethod, testInstance, statement);
//statement = withRulesReflectively(frameworkMethod, testInstance, statement);
//statement = withPotentialRepeat(frameworkMethod, testInstance, statement);
statement = withPotentialTimeout(frameworkMethod, testInstance, statement);
statement = withRepeatUntilSuccess(frameworkMethod, testInstance, statement);
return statement;
}
private Statement withRepeatUntilSuccess(FrameworkMethod frameworkMethod, Object testInstance, Statement next) {
return new CustomTestRepeat(next, frameworkMethod.getMethod(), Constants.MAX_NUM);
}
}
together with the following class
import java.lang.reflect.Method;
import org.junit.runners.model.Statement;
public class CustomTestRepeat extends Statement {
private final Statement next;
private final Method testMethod;
private final int repeat;
public CustomTestRepeat(Statement next, Method testMethod, int repeat) {
this.next = next;
this.testMethod = testMethod;
this.repeat = Math.max(1, repeat);
}
@Override
public void evaluate() throws Throwable {
for (int i = 0; i < this.repeat - 1; i++) {
try {
this.next.evaluate();
}
//Assertion errors are not instances of java.lang.Exception
catch (Throwable e) {
continue;
}
return;
}
this.next.evaluate();
}
}