Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Re-run failed test using Junit

I'm trying to improve an existing system of automated Selenium test. My goal is to repeat the tests that fails because of connections problem. I've found and tried to follow this thread How to Re-run failed JUnit tests immediately? that revealed itself quite useful.

In my case the suite is composed by classes, so I've tried to substitute @Rule with @ClassRule, in order to repeat for each try also the @Before and @After parts. I'm sorry for my ignorance, but where am I supposed to place this rule? In my Suite class? Or in the Classes representing the test?

like image 784
Lorm Avatar asked May 24 '13 15:05

Lorm


2 Answers

I am the original answerer of How to Re-run failed JUnit tests immediately?

If I understand correctly, the problem that you are having is due to the @Before being executed before the code in the RetryRule, and the @After being executed afterwards.

So your current behaviour is something like:

@Before
@Retry
test code
@Retry
@After

But you can implement your @Before and @After as a rule - there is a rule ExternalResource which does exactly that. You would implement @Before and @After as a rule:

@Rule public ExternalResource beforeAfter = new ExternalResource() {
    public void before() {
        // code that was in @Before
    }

    public void after() {
        // code that was in @After
    }
}

Then you don't need the @Before and @After. You can then chain these rules using RuleChain. This forces an order of execution to your rules:

@Rule public RuleChain chain= RuleChain
                       .outerRule(new LoggingRule("outer rule")
                       .around(new LoggingRule("middle rule")
                       .around(new LoggingRule("inner rule");

so your final solution would be something like:

private ExternalResource beforeAfter = ...
private RetryRule retry = ...

@Rule public RuleChain chain = RuleChain
                               .outerRule(retry)
                               .around(beforeAfter);

Note that if you are using RuleChain, you no longer need the @Rule annotation on the ExternalResource and RetryRule, but you do on the RuleChain.

like image 62
Matthew Farwell Avatar answered Nov 15 '22 16:11

Matthew Farwell


Here is my solution based on the one mentionned in the question.

It's a combinaison of a @Rule, FailedRule and a @ClassRule, RetryRule

public class RetryTest
{
    public static class FailedRule implements TestRule
    {       
        @Override
        public Statement apply(final Statement base, final Description description)
        {
             return new Statement()
             {
                    @Override
                    public void evaluate() throws Throwable
                    {
                        try
                        {
                            base.evaluate();
                        }
                        catch (Throwable t)
                        {
                            System.out.println(description.getDisplayName() + " failed");
                            retry.setNotGood();
                            if (retry.isLastTry())
                            {
                                System.out.println("No more retry !");
                                throw t;
                            }
                            else
                            {
                                System.out.println("Retrying.");
                            }
                        }
                    }
            };
        }
    }

    public static class RetryRule implements TestRule
    {
        private int retryCount, currentTry;

        private boolean allGood = false;

        public RetryRule(int retryCount)
        {
            this.retryCount = retryCount;
            this.currentTry = 1;
        }

        public boolean isLastTry()
        {
            return currentTry == retryCount;
        }

        public void setNotGood()
        {
            allGood = false;
        }

        public Statement apply(final Statement base, final Description description)
        {
            return new Statement()
            {
                @Override
                public void evaluate() throws Throwable
                {
                    // implement retry logic here
                    for (; currentTry <= retryCount && !allGood; currentTry++)
                    {
                        allGood = true;
                        System.out.println("Try #" + currentTry);
                        base.evaluate();
                    }
                }
            };
        }
    }

    @ClassRule
    public static RetryRule retry = new RetryRule(3);

    @Rule
    public FailedRule onFailed = new FailedRule();

    @BeforeClass
    public static void before()
    {
        System.out.println("Before...");
    }

    @AfterClass
    public static void after()
    {
        System.out.println("...After\n");
    }

    @Test
    public void test1()
    {
        System.out.println("> test1 running");
    }

    @Test
    public void test2()
    {
        System.out.println("> test2 running");
        Object o = null;
        o.equals("foo");
    }
}

It gives :

Try #1
Before...
> test1 running
> test2 running
test2(RetryTest) failed
Retrying.
...After

Try #2
Before...
> test1 running
> test2 running
test2(RetryTest) failed
Retrying.
...After

Try #3
Before...
> test1 running
> test2 running
test2(RetryTest) failed
No more retry !
...After

If I am commenting the o.equals("foo"); in test2, everything runs fine in the firt try :

Try #1
Before...
> test1 running
> test2 running
...After 
like image 37
ToYonos Avatar answered Nov 15 '22 16:11

ToYonos