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

2022-08-31 11:31:41

有没有办法有一个JUnit规则或类似的东西,给每个失败的测试第二次机会,只是尝试再次运行它。

背景:我有一大套用JUnit编写的Selenium2-WebDriver测试。由于非常激进的计时(仅在单击后等待很短的等待期),某些测试(100 个测试中的 1 个,并且始终是不同的测试)可能会失败,因为服务器有时响应速度稍慢。但是我不能让等待时间太长,以至于它绝对足够长,因为那样测试将永远需要。-- 所以我认为对于这个用例来说,测试是绿色的,即使它需要第二次尝试,也是可以接受的。

当然,最好有3个多数中的2个(重复失败的测试3次,如果其中两个测试是正确的,则将它们视为正确),但这将是未来的改进。


答案 1

您可以使用 TestRule 执行此操作。这将为您提供所需的灵活性。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");
    }
}

的核心是 ,它调用您的测试方法。因此,在此调用周围,您放置了一个重试循环。如果在测试方法中引发异常(断言失败实际上是 ),则测试失败,您将重试。TestRulebase.evaluate()AssertionError

还有一件事可能有用。您可能只想将此重试逻辑应用于一组测试,在这种情况下,您可以向 Retry 类中添加对方法上特定批注的测试。 包含方法的批注列表。有关此内容的更多信息,请参阅我对如何在每个 JUnit @Test 方法之前单独运行一些代码而不使用@RunWith和 AOP? 的回答。Description

使用自定义测试运行程序

这是CKuck的建议,你可以定义自己的Runner。您需要扩展BlockJUnit4ClassRunner并覆盖runChild()。有关详细信息,请参阅我对如何在套件中定义 JUnit 方法规则的回答?。此答案详细介绍了如何定义如何为套件中的每个方法运行代码,您必须为其定义自己的运行器。


答案 2

现在有更好的选择。如果您使用的是 maven 插件,例如:surfire 或 failedefe,则可以选择添加参数 SurFire Api。这些东西在以下工单中实现:Jira Ticket。在这种情况下,您不需要编写自定义代码和插件自动修改测试结果报告。
我只看到这种方法的一个缺点:如果在课前/课后阶段测试失败,则不会重新运行。rerunFailingTestsCount