29

We occasionally have bugs that appear once in every X runs. Before people check in stuff (where it is automatically JUnit'd), our devs need to pass JUnit locally via Eclipse.

Is there some convenient way (built in or high-quality Plugin) to make Eclipse run the same test X times and stop if there's a failure? An alternative to just clicking Run X times?

Note that I'm looking for something in the UI (e.g., right click and say "Run X times" instead of just "Run").

wuppi
  • 1,004
  • 10
  • 16
Uri
  • 88,451
  • 51
  • 221
  • 321
  • This is not really relevant to your question, but my curiosity is piqued - what kind of bugs are only appearing every so many runs? Is there some random element in the tests? Multiple threads maybe? I assume you've already tried to eliminate the randomness somehow. – Peter Recore Dec 02 '09 at 20:44
  • 1
    If you use Spring (or if the devs can add Spring locally) you can annotate the tests with @Repeat(number_of_runs). – laura Dec 02 '09 at 20:50
  • @Peter: Primarily things like race conditions and unclean states. We're testing one part of a system against a bunch of services that are outside our control. – Uri Dec 02 '09 at 21:53
  • Have a look here: http://stackoverflow.com/questions/8805305/in-eclipse-how-do-i-run-a-junit-test-case-multiple-times/9057363#9057363 – javanna Jan 30 '12 at 08:10
  • I need it to test threading issues, luckily after finding this post I discovered in IntellIJ you can repeat tests until failure by specifying that in the run configuration. Eclipse probably has something similar by now, this question is several years old. – ycomp Jan 15 '17 at 00:20
  • 1
    If this is a really important feature for you, fyi, IntilliJ has an explicit "Repeat: until failure" option in its run configurations. – b15 Apr 26 '19 at 16:59

7 Answers7

12

If the for loop works, then I agree with nos.

If you need to repeat the entire setup-test-teardown, then you can use a TestSuite:

  1. Right-click on the package containing the test to repeat
  2. Go to New and choose to create a JUnit test SUITE
  3. Make sure that only the test you want to repeat is selected and click through to finish.
  4. Edit the file to run it multiple times.

In the file you just find the

addTestSuite(YourTestClass.class)

line, and wrap that in a for loop.

I'm pretty sure that you can use addTest instead of addTestSuite to get it to only run one test from that class if you just want to repeat a single test method.

Michael Rusch
  • 2,479
  • 2
  • 16
  • 20
  • Yea, I tried something like this. But I was hoping for something directly in the UI. – Uri Dec 02 '09 at 20:39
  • I wouldn't be surprised if it's different in JUnit 4 (I haven't tried it). But, the basic idea is to let Eclipse make the first instance of the test, and then you replicate what they did multiple times, so I would think there would be an equivalent JUnit 4 solution – Michael Rusch Jan 20 '13 at 22:18
8

If you really want to run a test class until failure, you need your own runner.

@RunWith(RunUntilFailure.class)
public class YourClass {

    // ....

}

which could be implemented as follows...

package com.example;

import org.junit.internal.runners.*;
import org.junit.runner.notification.*;
import org.junit.runner.*;

public class RunUntilFailure extends Runner {

    private TestClassRunner runner;

    public RunUntilFailure(Class<?> klass) throws InitializationError {
        this.runner = new TestClassRunner(klass);
    }

    @Override
    public Description getDescription() {
        Description description = Description.createSuiteDescription("Run until failure");
        description.addChild(runner.getDescription());
        return description;
    }

    @Override
    public void run(RunNotifier notifier) {
        class L extends RunListener {
            boolean fail = false;
            public void testFailure(Failure failure) throws Exception { fail = true; }
        }
        L listener = new L();
        notifier.addListener(listener);
        while (!listener.fail) runner.run(notifier);
    }

}

...releasing untested code, feeling TDD guilt :)

akuhn
  • 27,477
  • 2
  • 76
  • 91
4

Based on @akuhn's answer, here is what I came up with - rather than running forever, this will run 50 times or until failure, whichever comes first.

package com.foo    

import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runner.notification.Failure;
import org.junit.runner.notification.RunListener;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.InitializationError;

public class RunManyTimesUntilFailure extends Runner {

    private static final int MAX_RUN_COUNT = 50;

    private BlockJUnit4ClassRunner runner;

    @SuppressWarnings("unchecked")
    public RunManyTimesUntilFailure(final Class testClass) throws InitializationError {
        runner = new BlockJUnit4ClassRunner(testClass); 
    }

    @Override
    public Description getDescription() {
        final Description description = Description.createSuiteDescription("Run many times until failure");
        description.addChild(runner.getDescription());
        return description;
    }

    @Override
    public void run(final RunNotifier notifier) {
        class L extends RunListener {
            boolean shouldContinue = true;
            int runCount = 0;

            @Override
            public void testFailure(@SuppressWarnings("unused") final Failure failure) throws Exception {
                shouldContinue = false;
            }

            @Override
            public void testFinished(@SuppressWarnings("unused") Description description) throws Exception {
                runCount++;

                shouldContinue = (shouldContinue && runCount < MAX_RUN_COUNT);
            }
        }

        final L listener = new L();
        notifier.addListener(listener);

        while (listener.shouldContinue) {
            runner.run(notifier);
        }
    }
}
benvolioT
  • 4,507
  • 2
  • 36
  • 30
3

I know it doesn't answer the question directly but if a test isn't passing every time it is run it is a test smell known as Erratic Test. There are several possible causes for this (from xUnit Test Patterns):

  • Interacting Tests
  • Interacting Test Suites
  • Lonely Test
  • Resource Leakage
  • Resource Optimism
  • Unrepeatable Test
  • Test Run War
  • Nondeterministic Test

The details of each of these is documented in Chapter 16 of xUnit Test Patterns.

Kenneth Cochran
  • 11,954
  • 3
  • 52
  • 117
3

Here is a post I wrote that shows several ways of running the tests repeatedly with code examples: http://codehowtos.blogspot.com/2011/04/run-junit-test-repeatedly.html

You can use the @Parametrized runner, or use the special runner included in the post There is also a reference to a @Retry implementation

Kreich
  • 249
  • 3
  • 5
2

I don't believe there's a built in way for junit to do exactly what you're asking for.

If multiple runs produce different result, you should have a unit test testing that case. Wich might be as simple as running a for loop in the relevant test cases.

nos
  • 223,662
  • 58
  • 417
  • 506
  • 1
    Yea, but we've got a whole test suite where multiple people keep adding tests... On the build server we do have a loop... I was hoping Eclipse had something. – Uri Dec 02 '09 at 20:34
2

It is possible to implement such an loop with TestRules (since JUnit 4.9)

A very simple implementation that runs every Test 10 times:

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

public class SimpleRepeatRule implements TestRule {

    private static class SimpleRepeatStatement extends Statement {

        private final Statement statement;

        private SimpleRepeatStatement(Statement statement) {
            this.statement = statement;
        }

        @Override
        public void evaluate() throws Throwable {
            for (int i = 0; i < 10; i++) {
                statement.evaluate();
            }
        }
    }

    @Override
    public Statement apply(Statement statement, Description description) {
        return new SimpleRepeatStatement(statement);
    }
}

usage:

public class Run10TimesTest {

   @Rule
   public SimpleRepeatRule repeatRule = new SimpleRepeatRule();

   @Test
   public void myTest(){...}
}

For a more useful implementation based on an annotation that define which test method has to been executed how often have a look at this blog: http://www.codeaffine.com/2013/04/10/running-junit-tests-repeatedly-without-loops/

Ralph
  • 118,862
  • 56
  • 287
  • 383