views:

426

answers:

5

Like the title says, I'm looking for some simple way to run JUnit 4.x tests several times in a row automatically using Eclipse.

An example would be running the same test 10 times in a row and reporting back the result.

We already have a complex way of doing this but I'm looking for a simple way of doing it so that I can be sorta sure that the flaky test I've been trying to fix stays fixed.

An ideal solution would be an Eclipse plugin/setting/feature that I am unaware of.

A: 

I've found that Spring's repeat annotation is useful for that kind of thing:

@Repeat(value = 10)

http://static.springsource.org/spring/docs/2.5.6/reference/testing.html http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/annotation/Repeat.html

laura
Changing test frameworks is not what I would call an easy way of doing it.
Stefan Thyberg
+1  A: 

Does running a test 'n' times really indicate that you've fixed this problem ?

So something in your code is non-deterministic. Can you mock that out ? Can you extract the functionality unrelated to the non-deterministic code and unit test that ? Can you remove the uncertainty altogether (e.g. if it's threading related, can you make use of queues etc.)

Otherwise you can run your test 10 times, and still have no confidence in it.

Brian Agnew
This is not supposed to be a definite test. Strictly speaking, these are not unittests but kind of big black box tests.
Stefan Thyberg
There are several use-cases of non-deterministic code that you can unit test using this method. For example, what if you have a container that serves objects from the database, each with a certain probability: some are more probable than others but you have to maintain an all around average. That's one situation where you'd want to run a test several times before you can claim you're getting expected results
laura
@laura - granted, but testing a probability distribution is a pretty specialised case.
Brian Agnew
Running a test several times is also a way of finding out IF you have something undeterministic in your test case to begin with.
Stefan Thyberg
@Stefan The problem with that is that the test will never tell you that the problem is fixed, just that it exists.
aperkins
Which is good enough for me.
Stefan Thyberg
+1  A: 

The easiest (as in least amount of new code required) way to do this is to run the test as a parametrized test (annotate with an @RunWith(Parameterized.class) and add a method to provide 10 empty parameters). That way the framework will run the test 10 times.

This test would need to be the only test in the class, or better put all test methods should need to be run 10 times in the class.

Here is an example:

@RunWith(Parameterized.class)
public class RunTenTimes {
    @Parameterized.Parameters
    public static List<Object[]> data() {
        return Arrays.asList(new Object[10][0]);
    }

    public RunTenTimes() {
    }

    @Test
    public void runsTenTimes() {
        System.out.println("run");
    }
}

EDIT: With the above, it is possible to even do it with a parameterless constructor, but I'm not sure if the framework authors intended that, or if that will break in the future.

EDIT (in response to comment): If you are implementing your own runner, then you could have the runner run the test 10 times. If you are using a third party runner, then with 4.7, you can use the new @Rule annotation and implement the MethodRule interface so that it takes the statement and executes it 10 times in a for loop. The current disadvantage of this approach is that @Before and @After get run only once. This will likely change in the next version of JUnit (the @Before will run after the @Rule), but regardless you will be acting on the same instance of the object (something that isn't true of the Parameterized runner). This assumes that whatever runner you are running the class with correctly recognizes the @Rule annotations. That is only the case if it is delegating to the JUnit runners.

If you are running with a custom runner that does not recognize the @Rule annotation, then you are really stuck with having to write your own runner that delegates appropriately to that Runner and runs it 10 times.

Note that there are other ways to potentially solve this (such as the Theories runner) but they all require a runner. Unfortunately JUnit does not currently support layers of runners. That is a runner that chains other runners.

Yishai
Holy crap, I think you may have fixed a gripe I have had about JUnit for a long time - thanks! :)
aperkins
Unfortunately I'm already running @RunWith with another runner, but otherwise this would have been an ideal solution.
Stefan Thyberg
Yes, this is the solution that I would like to have though and which will be best for most people so I'm going to go ahead and accept the answer.
Stefan Thyberg
A: 

Anything wrong with:

@Test
void itWorks()
{
    // stuff
}

@Test
void itWorksRepeatably()
{
    for (int i = 0; i < 10; i++)
    {
        itWorks();
    }
}

Unlike the case where you are testing each of an array of values, you don't particularly care which run failed.

No need to do in configuration or annotation what you can do in code.

soru
I'd like to run several tests as normal unit tests and get a trace and status for each one.
Stefan Thyberg
A: 

There's an Intermittent annotation in the tempus-fugit library which works with JUnit 4.7's @Rule to repeat a test several times or with @RunWith.

For example,

@RunWith(IntermittentTestRunner.class)
public class IntermittentTestRunnerTest {

   private static int testCounter = 0;

   @Test
   @Intermittent(repition = 99)
   public void annotatedTest() {
      testCounter++;
   }
}

After the test is run (with the IntermittentTestRunner in the @RunWith), testCounter would be equal to 99.

Toby
Yeah, it's the same problem here, already using another runner and so can't use this one, good idea though.
Stefan Thyberg
Yeah, I'm having the same issue with RunWith...as it goes I tweaked tempus-fugit to get round it a little, you can use a @Rule rather than runner when you want to run repeatedly. You mark it up with @Repeating instead of intermittent. The rule version wont run @Before/@Afters though.See http://tempus-fugit.googlecode.com/svn/site/documentation/concurrency.html#JUnit_Integration (scroll down to load/soak testing) for more details.
Toby