I'm trying to create test automation suite using JUnit. For all the tests, I want to create a Rule, for this I have created an Interface and placed the Rule inside it. Any tests that I want to run will have to implent that interface. It didn't throw any compiler error, however, when my Test class implements that interface, this doesn't seem to work. Following is the Interface I created.
public interface IBaseTest {
@Rule
public TestRule test = new TestWatcher(){
@Override
protected void starting(Description description)
{
System.out.println("Starting Test: " + description);
}
};
}
Alternatively, I could have created the above as a class and extend all my test classes from that class, I tried that and it worked perfectly, however this would prevent me from extending my test methods from any other class.
Is there a way for me to create rules that will be applicable for all my tests without extending from a base class?
Yes there is a way that I am aware of but it will make you write some extra code.
First, the reason that JUnit is ignoring your TestRule is because it is declared on an interface hence static (and final).
To overcome this issue one will need to write a custom runner like this:
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;
public final class MyRunner extends BlockJUnit4ClassRunner {
public MyRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected List<TestRule> getTestRules(Object target) {
List<TestRule> testRules = super.getTestRules(target);
testRules.addAll(getStaticFieldTestRules(target));
return testRules;
}
private List<TestRule> getStaticFieldTestRules(Object target) {
List<TestRule> testRules = new ArrayList<>();
Class<?> clazz = target.getClass();
for (Field f : clazz.getFields()) {
if ((f.getModifiers() & Modifier.STATIC) != 0) {
if (f.isAnnotationPresent(Rule.class)) {
try {
testRules.add((TestRule) f.get(target));
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
}
return testRules;
}
}
Finally, annotate your test class to run with the new custom runner and all is as you expect...
import org.junit.runner.RunWith;
@RunWith(MyRunner.class)
public class Test implements IBaseTest {
@org.junit.Test
public void testName1() throws Exception {
}
@org.junit.Test
public void testName2() throws Exception {
}
}