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



 
