After reading abyx's post on Retry Rule, I decided to write a post about running repeating tests with JUnit.
Hope you enjoy this:
Sometimes you wish to run a test many times. For example, it might have some random factor in it. To cover many cases, you'd like to run it a lot of times. What options are there?
First, and the most obvious, is a loop. Run a for loop, and see it fail:
This will generate a single test in the tree. It will fail on the first error and won't give you an assessment of how many times it took till it failed.public class ExampleTest { @Test public void sometimesFail() { for (int i = 0; i < 10; i++) { int rand = new Random().nextInt(3); if (rand % 3 == 0) { fail(); } } } }
Another option is the Parametrized Runner. That would look like this:
This is a nice solution, but the test is filled with unnecessary code. Also, all the tests in the class will run for each parameter. What if I only want to repeat a single method?@RunWith(Parameterized.class) public class ExampleTest2 { @Parameters public static Collection<Object[]> generateParams() { List<Object[]> params = new ArrayList<Object[]>(); for (int i = 1; i <= 10; i++) { params.add(new Object[] {i}); } return params; } public ExampleTest2(int param) {} @Test public void sometimesFail() { int rand = new Random().nextInt(3); if (rand % 3 == 0) { fail(); } } }
Here is a nice solution, in my opinion:
Only state the method you want to repeat, and how many times to do this. The rest is done for you.@RunWith(ExtendedRunner.class) public class ExampleTest3 { @Test @Repeat(10) public void sometimesFail() { int rand = new Random().nextInt(3); if (rand % 3 == 0) { fail(); } } }
So, whats behind this code?
First, add an Annotation:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Repeat {
int value();
}
Finally, lets add the Runner. This simply overrides the default JUnit runner (its a bit long, but not too much):
public class ExtendedRunner extends BlockJUnit4ClassRunner {
public ExtendedRunner(Class<?> klass) throws InitializationError {
super(klass);
}
@Override
protected Description describeChild(FrameworkMethod method) {
if (method.getAnnotation(Repeat.class) != null &&
method.getAnnotation(Ignore.class) == null) {
return describeRepeatTest(method);
}
return super.describeChild(method);
}
private Description describeRepeatTest(FrameworkMethod method) {
int times = method.getAnnotation(Repeat.class).value();
Description description = Description.createSuiteDescription(
testName(method) + " [" + times + " times]",
method.getAnnotations());
for (int i = 1; i <= times; i++) {
description.addChild(Description.createTestDescription(
getTestClass().getJavaClass(),
"[" + i + "] " + testName(method)));
}
return description;
}
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (method.getAnnotation(Repeat.class) != null &&
method.getAnnotation(Ignore.class) == null) {
runRepeatedly(methodBlock(method), description, notifier);
}
super.runChild(method, notifier);
}
private void runRepeatedly(Statement statement, Description description,
RunNotifier notifier) {
for (Description desc : description.getChildren()) {
runLeaf(statement, desc, notifier);
}
}
}
As always, here is the source code: http://tinyurl.com/3mu2zbc
Also, you can Search Amazon.com for JUnit books