Skip to content

Instantly share code, notes, and snippets.

@abyx
Created March 31, 2011 20:51
Show Gist options
  • Save abyx/897229 to your computer and use it in GitHub Desktop.
Save abyx/897229 to your computer and use it in GitHub Desktop.
Simple JUnit rule to make tests retry
public class RetrierTest {
private static int count = 0;
@Rule public RetryRule rule = new RetryRule();
@Test
@Retry
public void failsFirst() throws Exception {
count++;
assertEquals(2, count);
}
}
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {}
public class RetryRule implements MethodRule {
@Override public Statement apply(final Statement base, final FrameworkMethod method, Object target) {
return new Statement() {
@Override public void evaluate() throws Throwable {
try {
base.evaluate();
} catch (Throwable t) {
Retry retry = method.getAnnotation(Retry.class);
if (retry != null) {
base.evaluate();
} else {
throw t;
}
}
}
};
}
}
@braghome
Copy link

braghome commented Jun 5, 2015

what if their is a requirement to only retry till count of 3

@jaredsburrows
Copy link

In Kotlin:

RetryRule:

import org.junit.rules.MethodRule
import org.junit.runners.model.FrameworkMethod
import org.junit.runners.model.Statement

class RetryRule : MethodRule {
    override fun apply(base: Statement, method: FrameworkMethod, target: Any): Statement {
        return object : Statement() {
            override fun evaluate() {
                try {
                    base.evaluate()
                } catch (t: Throwable) {
                    val retry = method.getAnnotation(Retry::class.java)
                    if (retry != null) {
                        base.evaluate()
                    } else {
                        throw t
                    }
                }

            }
        }
    }
}

RetrierTest:

import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test

class RetrierTest {
    companion object {
        private var count = 0
    }

    @get:Rule var rule = RetryRule()

    @Test
    @Retry
    @Throws(Exception::class)
    fun failsFirst() {
        count++
        assertEquals(2, count)
    }
}

Retry:

@Retention(AnnotationRetention.RUNTIME)
annotation class Retry

@AndreSand
Copy link

AndreSand commented Mar 1, 2019

Below code snippet is if you want to retry tests X number.

RetryKotlin.kt


/**
 * Retry test rule used to retry test that failed. Retry failed test 3 times
 */
class RetryTestRule(val retryCount: Int = 3) : TestRule {

    private val TAG = RetryTestRule::class.java.simpleName

    override fun apply(base: Statement, description: Description): Statement {
        return statement(base, description)
    }

    private fun statement(base: Statement, description: Description): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                var caughtThrowable: Throwable? = null

                // implement retry logic here
                for (i in 0 until retryCount) {
                    try {
                        base.evaluate()
                        return
                    } catch (t: Throwable) {
                        caughtThrowable = t
                        Log.e(TAG, description.displayName + ": run " + (i + 1) + " failed")
                    }
                }

                Log.e(TAG, description.displayName + ": giving up after " + retryCount + " failures")
                throw caughtThrowable!!
            }
        }
    }
}


Add rule to tests class:

    @Rule
    @JvmField
    val mRetryTestRule = RetryTestRule()

You can add retry Rule to Android espresso tests.

@ashokdaukiya
Copy link

not working for me test get failed but it will not retry
error androidTest.RetryRule$1.evaluate(RetryRule.java:16)
junit.framework.AssertionFailedError: expected:<5> but was:<2>

@tomdwp
Copy link

tomdwp commented Mar 16, 2021

@AndreSand is there a way to adapt your snippet to work on a per-test basis? (instead of a per-test-class basis)

@a-oleynik
Copy link

@tomdwp
public class RetryRule implements MethodRule {

private AtomicInteger retryCount;

public RetryRule() {
    this(3);
}

public RetryRule(int retries) {
    super();
    this.retryCount = new AtomicInteger(retries);
}

@Override
public Statement apply(final Statement base, final FrameworkMethod method, Object target) {
    return new Statement() {
        @Override
        public void evaluate() throws Throwable {
            Throwable caughtThrowable = null;

            while (retryCount.getAndDecrement() > 0) {
                try {
                    base.evaluate();
                    return;
                } catch (Throwable t) {
                    if (retryCount.get() > 0 &&
                            method.getAnnotation(Retry.class) != null) {
                        caughtThrowable = t;
                        System.err.println(
                                method.getName() +
                                        ": Failed, " +
                                        retryCount.toString() +
                                        "retries remain");
                    } else {
                        throw caughtThrowable;
                    }
                }
            }
        }
    };
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment