Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sql-server-2005/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何立即重新运行失败的JUnit测试?_Java_Testing_Junit - Fatal编程技术网

Java 如何立即重新运行失败的JUnit测试?

Java 如何立即重新运行失败的JUnit测试?,java,testing,junit,Java,Testing,Junit,有没有一种方法可以使用JUnit规则或类似的东西,让每一个失败的测试都有第二次机会,只需再次尝试运行它 背景:我用JUnit编写了大量Selenium2 WebDriver测试。由于非常激进的计时(单击后只有很短的等待时间),一些测试(100个测试中有1个,并且总是不同的测试)可能会失败,因为服务器有时响应较慢。但是我不能让等待时间太长以至于它肯定足够长,因为这样测试将永远持续下去。)--因此我认为对于这个用例来说,绿色测试是可以接受的,即使它需要第二次尝试 当然,最好是三分之二的多数(重复一个

有没有一种方法可以使用JUnit规则或类似的东西,让每一个失败的测试都有第二次机会,只需再次尝试运行它

背景:我用JUnit编写了大量Selenium2 WebDriver测试。由于非常激进的计时(单击后只有很短的等待时间),一些测试(100个测试中有1个,并且总是不同的测试)可能会失败,因为服务器有时响应较慢。但是我不能让等待时间太长以至于它肯定足够长,因为这样测试将永远持续下去。)--因此我认为对于这个用例来说,绿色测试是可以接受的,即使它需要第二次尝试


当然,最好是三分之二的多数(重复一个失败的测试三次,如果其中两个测试是正确的,就认为它们是正确的),但这将是一个未来的改进。

您必须编写自己的
org.junit.runner.runner
,并用
@RunWith(YourRunner.class)注释您的测试

您可以使用一个。这将为您提供所需的灵活性。TestRule允许您在测试周围插入逻辑,以便实现重试循环:

public class RetryTest {
    public class Retry implements TestRule {
        private int retryCount;

        public Retry(int retryCount) {
            this.retryCount = retryCount;
        }

        public Statement apply(Statement base, Description description) {
            return statement(base, description);
        }

        private Statement statement(final Statement base, final Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    Throwable caughtThrowable = null;

                    // implement retry logic here
                    for (int i = 0; i < retryCount; i++) {
                        try {
                            base.evaluate();
                            return;
                        } catch (Throwable t) {
                            caughtThrowable = t;
                            System.err.println(description.getDisplayName() + ": run " + (i+1) + " failed");
                        }
                    }
                    System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures");
                    throw caughtThrowable;
                }
            };
        }
    }

    @Rule
    public Retry retry = new Retry(3);

    @Test
    public void test1() {
    }

    @Test
    public void test2() {
        Object o = null;
        o.equals("foo");
    }
}
公共类RetryTest{
公共类重试实现TestRule{
私人内部检索计数;
公共重试(int retryCount){
this.retryCount=retryCount;
}
公开声明应用(声明库、说明){
返回语句(基、描述);
}
专用报表(最终报表库、最终说明){
返回新语句(){
@凌驾
public void evaluate()可丢弃{
Throwable caughtThrowable=null;
//在这里实现重试逻辑
for(int i=0;i
TestRule
的核心是调用测试方法的
base.evaluate()
。因此,围绕这个调用,您将设置一个重试循环。如果在测试方法中抛出异常(断言失败实际上是
AssertionError
),则测试失败,您将重试

还有一件事可能有用。您可能只希望将此重试逻辑应用于一组测试,在这种情况下,您可以将方法上特定注释的测试添加到重试类之上<代码>说明
包含方法的注释列表。有关这方面的更多信息,请参阅我对的回答

使用自定义TestRunner
这是CKuck的建议,您可以定义自己的跑步者。您需要扩展并重写runChild()。有关更多信息,请参阅我对的回答。这个答案详细说明了如何定义如何为套件中的每个方法运行代码,为此您必须定义自己的运行程序。

至于我编写自定义运行程序更灵活的解决方案。 上面发布的解决方案(带有代码示例)有两个缺点:

  • 如果在@BeforeClass阶段失败,则不会重试测试
  • 计算测试的运行方式有点不同(当您有3次重试时, 您将收到测试运行:4,成功1,可能会令人困惑) 这就是为什么我更喜欢编写自定义runner的方法。自定义运行程序的代码可以如下所示:

    import org.junit.Ignore;
    import org.junit.internal.AssumptionViolatedException;
    import org.junit.internal.runners.model.EachTestNotifier;
    import org.junit.runner.Description;
    import org.junit.runner.notification.RunNotifier;
    import org.junit.runner.notification.StoppedByUserException;
    import org.junit.runners.BlockJUnit4ClassRunner;
    import org.junit.runners.model.FrameworkMethod;
    import org.junit.runners.model.InitializationError;
    import org.junit.runners.model.Statement;
    
    
    public class RetryRunner extends BlockJUnit4ClassRunner {
    
        private final int retryCount = 100;
        private int failedAttempts = 0;
    
        public RetryRunner(Class<?> klass) throws InitializationError {
            super(klass);
        }    
    
    
        @Override
        public void run(final RunNotifier notifier) {
            EachTestNotifier testNotifier = new EachTestNotifier(notifier,
                    getDescription());
            Statement statement = classBlock(notifier);
            try {
    
                statement.evaluate();
            } catch (AssumptionViolatedException e) {
                testNotifier.fireTestIgnored();
            } catch (StoppedByUserException e) {
                throw e;
            } catch (Throwable e) {
                retry(testNotifier, statement, e);
            }
        }
    
        @Override
        protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
            Description description = describeChild(method);
            if (method.getAnnotation(Ignore.class) != null) {
                notifier.fireTestIgnored(description);
            } else {
                runTestUnit(methodBlock(method), description, notifier);
            }
        }
    
        /**
         * Runs a {@link Statement} that represents a leaf (aka atomic) test.
         */
        protected final void runTestUnit(Statement statement, Description description,
                RunNotifier notifier) {
            EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
            eachNotifier.fireTestStarted();
            try {
                statement.evaluate();
            } catch (AssumptionViolatedException e) {
                eachNotifier.addFailedAssumption(e);
            } catch (Throwable e) {
                retry(eachNotifier, statement, e);
            } finally {
                eachNotifier.fireTestFinished();
            }
        }
    
        public void retry(EachTestNotifier notifier, Statement statement, Throwable currentThrowable) {
            Throwable caughtThrowable = currentThrowable;
            while (retryCount > failedAttempts) {
                try {
                    statement.evaluate();
                    return;
                } catch (Throwable t) {
                    failedAttempts++;
                    caughtThrowable = t;
                }
            }
            notifier.addFailure(caughtThrowable);
        }
    }
    
    import org.junit.Ignore;
    导入org.junit.internal.AssumptionViolatedException;
    导入org.junit.internal.runners.model.EachTestNotifier;
    导入org.junit.runner.Description;
    导入org.junit.runner.notification.RunNotifier;
    导入org.junit.runner.notification.StoppedByUserException;
    导入org.junit.runners.BlockJUnit4ClassRunner;
    导入org.junit.runners.model.FrameworkMethod;
    导入org.junit.runners.model.InitializationError;
    导入org.junit.runners.model.Statement;
    公共类RetryRunner扩展了BlockJUnit4ClassRunner{
    私人最终int retryCount=100;
    专用int failedAttempts=0;
    public RetryRunner(类klass)抛出初始化错误{
    超级(klass);;
    }    
    @凌驾
    公共无效运行(最终运行通知程序通知程序){
    EachTestNotifier testNotifier=新的EachTestNotifier(通知程序,
    getDescription());
    语句=类块(通知程序);
    试一试{
    语句。evaluate();
    }捕获(假设违反异常e){
    testNotifier.fireTestIgnored();
    }捕获(由用户异常停止){
    投掷e;
    }捕获(可丢弃的e){
    重试(testNotifier,语句,e);
    }
    }
    @凌驾
    受保护的void runChild(最终框架方法,RunNotifier通知程序){
    描述=描述儿童(方法);
    if(method.getAnnotation(Ignore.class)!=null){
    通知程序。fireTestIgnored(说明);
    }否则{
    runTestUnit(methodBlock(方法)、说明、通知程序);
    }
    }
    /**
    *运行表示叶(又名原子)测试的{@link语句}。
    */
    受保护的最终无效运行测试单元(语句、说明、,
    运行通知程序(通知程序){
    EachTestNotifier eachNotifier=新的EachTestNotifier(通知程序,说明)
    
    import org.junit.rules.TestRule;
    import org.junit.runner.Description;
    import org.junit.runners.model.Statement;
    
    public class RetryRule implements TestRule {
        private int retryCount;
    
        public RetryRule (int retryCount) {
            this.retryCount = retryCount;
        }
    
        public Statement apply(Statement base, Description description) {
            return statement(base, description);
        }
    
        private Statement statement(final Statement base, final Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    Throwable caughtThrowable = null;
    
                    // implement retry logic here
                    for (int i = 0; i < retryCount; i++) {
                        try {
                            base.evaluate();
                            return;
                        } catch (Throwable t) {
                            caughtThrowable = t;
                            //  System.out.println(": run " + (i+1) + " failed");
                            System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed.");
                        }
                    }
                    System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures.");
                    throw caughtThrowable;
                }
            };
        }
    }
    
    import org.junit.BeforeClass;
    import org.junit.Rule;
    import org.junit.Test;
    import org.openqa.selenium.WebDriver;
    import org.openqa.selenium.firefox.FirefoxDriver;
    import static org.hamcrest.CoreMatchers.is;
    import static org.hamcrest.MatcherAssert.assertThat;
    
    /**
     * Created by ONUR BASKIRT on 27.03.2016.
     */
    public class RetryRuleTest {
    
        static WebDriver driver;
        final private String URL = "http://www.swtestacademy.com";
    
        @BeforeClass
        public static void setupTest(){
            driver = new FirefoxDriver();
        }
    
        //Add this notification to your Test Class 
        @Rule
        public RetryRule retryRule = new RetryRule(3);
    
        @Test
        public void getURLExample() {
            //Go to www.swtestacademy.com
            driver.get(URL);
    
            //Check title is correct
            assertThat(driver.getTitle(), is("WRONG TITLE"));
        }
    }
    
    public final class RetryRule<A extends Activity> implements TestRule {
        private final int retryCount;
        private final Class<A> activityClazz;
        private ActivityScenario<A> scenario;
    
        /**
         * @param retryCount the number of retries. retryCount = 1 means 1 (normal) try and then
         * 1 retry, i.e. 2 tries overall
         */
        public RetryRule(int retryCount, @NonNull Class<A> clazz) {
            this.retryCount = retryCount;
            this.activityClazz = clazz;
        }
    
        public Statement apply(Statement base, Description description) {
            return statement(base, description);
        }
    
        private Statement statement(final Statement base, final Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    Throwable caughtThrowable = null;
    
                    // implement retry logic here
                    for (int i = 0; i <= retryCount; i++) {
                        try(ActivityScenario<A> scenario = ActivityScenario.launch(activityClazz)){
                            RetryRule.this.scenario = scenario;
                            base.evaluate();
                            return;
                        } catch (Throwable t) {
                            caughtThrowable = t;
                            Log.e(LOGTAG,
                                    description.getDisplayName() + ": run " + (i + 1) + " failed: ", t);
                        }
                    }
                    Log.e(LOGTAG,
                            description.getDisplayName() + ": giving up after " + (retryCount + 1) +
                                    " failures");
                    throw Objects.requireNonNull(caughtThrowable);
                }
            };
        }
    
        public ActivityScenario<A> getScenario() {
            return scenario;
        }
    }