Reputation: 14439
I try to write own junit runner and currently I am stuck at returning proper test description.
public class ParameterizedWrapper extends Suite {
private List<Runner> fRunners;
/**
* @throws Throwable
*
*/
public ParameterizedWrapper(final Class<?> clazz) throws Throwable {
super(clazz, Collections.<Runner>emptyList());
fRunners = constructRunners(getParametersMethod());
}
protected List<Runner> constructRunners(final FrameworkMethod method) throws Exception, Throwable {
@SuppressWarnings("unchecked")
Iterable<Object[]> parameters = (Iterable<Object[]>) getParametersMethod().invokeExplosively(null);
ArrayList<Runner> runners = new ArrayList<Runner>();
int index = 0;
for (Object[] parameter : parameters) {
Class<?> testClass = getTestClass().getJavaClass();
WrappedRunner wrappedRunner = testClass.getAnnotation(WrappedRunner.class);
Runner runner = wrappedRunner.value().getConstructor(Class.class).newInstance(getTestClass().getJavaClass());
runners.add(new WrappingRunner(runner, parameter, testClass, index++));
}
return runners;
}
private FrameworkMethod getParametersMethod() throws Exception {
List<FrameworkMethod> methods = getTestClass().getAnnotatedMethods(Parameters.class);
for (FrameworkMethod each : methods) {
if (each.isStatic() && each.isPublic()) {
return each;
}
}
throw new Exception("No public static parameters method on class " + getTestClass().getName());
}
@Override
protected List<Runner> getChildren() {
return fRunners;
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public static @interface WrappedRunner {
Class<? extends Runner> value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public static @interface ParameterSetter {
}
}
class WrappingRunner extends Runner {
private Runner wrappedRunner;
private Object[] parameters;
private Class<?> testClass;
private int testPosition;
public WrappingRunner(final Runner runner, final Object[] params, final Class<?> clazz, final int position) {
wrappedRunner = runner;
parameters = params;
testClass = clazz;
testPosition = position;
}
@Override
public Description getDescription() {
Description originalDescription = wrappedRunner.getDescription();
Description newDescription = Description.createSuiteDescription(nameFor(""), new Annotation[0]);
for (Description child : originalDescription.getChildren()) {
newDescription.addChild(decorateChildDescription(child));
}
return newDescription;
}
private String nameFor(String name) {
return String.format("%1$s[%2$s]", name, testPosition);
}
protected Description decorateChildDescription(final Description originalChildDescription) {
Description d = Description.createTestDescription(originalChildDescription.getTestClass(),
nameFor(originalChildDescription.getMethodName()),
originalChildDescription.getAnnotations().toArray(new Annotation[0]));
return d;
}
@Override
public void run(final RunNotifier notifier) {
try {
ParameterStorage.storeParameters(testClass, parameters);
wrappedRunner.run(notifier);
} finally {
ParameterStorage.clearParameters(testClass);
}
}
}
I have some test class to check if runner works. Runner works fine except tests are named weirdly. In eclipse it displays all tests and adds unrooted tests category
and surefire does not use my naming at all:
I compared description objects generated in my runner and in Parameterized
runner, there seems to be no difference.
Upvotes: 0
Views: 315
Reputation: 24286
It's a bit ugly, but it's safer to pass the list of child runners to the parent constructor:
public ParameterizedWrapper(final Class<?> clazz) throws Throwable {
super(clazz, constructRunners(getParametersMethod());
}
private static List<Runner> constructRunners(final FrameworkMethod method)
throws Throwable {
...
You shouldn't need to override Suite.getChildren()
Upvotes: 1
Reputation: 14439
I have checked a bit more and found that description generated by my runner is ok. But that does not matter as it is inconsistent with description used during actual test execution. That is why eclipse shows entries as not executed and that is why surefire does not show my names.
Currently I think to use my own notifier to catch test start point and replace configuration at that point.
If someone has a better solution I would like to know about it :).
Upvotes: 0