【问题标题】:Re-run failed test using Junit使用 Junit 重新运行失败的测试
【发布时间】:2013-05-20 07:01:33
【问题描述】:

我正在尝试改进现有的自动化 Selenium 测试系统。 我的目标是重复由于连接问题而失败的测试。 我已经找到并尝试关注这个帖子 How to Re-run failed JUnit tests immediately?,这表明它非常有用。

在我的例子中,套件是由类组成的,所以我尝试用@ClassRule 替换@Rule,以便在每次尝试时重复@Before 和@After 部分。 我很抱歉我的无知,但我应该把这条规则放在哪里?在我的套房课上?还是在代表测试的类中?

【问题讨论】:

    标签: java testing junit


    【解决方案1】:

    我是How to Re-run failed JUnit tests immediately?的原始回复者

    如果我理解正确,您遇到的问题是由于@BeforeRetryRule 中的代码之前执行,而@After 在之后执行。

    所以你目前的行为是这样的:

    @Before
    @Retry
    test code
    @Retry
    @After
    

    但是您可以将@Before@After 作为一项规则来实施- 有一条规则ExternalResource 正是这样做的。您通常会实现@Before@After

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

    那么您就不需要@Before@After。然后,您可以使用RuleChain 链接这些规则。这会强制执行您的规则:

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

    所以你的最终解决方案是这样的:

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

    请注意,如果您使用RuleChain,则不再需要ExternalResourceRetryRule 上的@Rule 注释,但您需要RuleChain

    【讨论】:

    • 但是在这个解决方案中,每个测试都会调用 before 和 after,而不是所有测试的一次?这可能有问题。
    • @Before/@After 为每个测试运行 - 据我所知,这就是 OP 想要的
    【解决方案2】:

    这是我基于问题中提到的解决方案。

    它是 @RuleFailedRule@ClassRuleRetryRule

    的组合
    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");
        }
    }
    

    它给出了:

    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
    

    如果我在test2 中评论o.equals("foo");,一切都会在第一次尝试中正常运行:

    Try #1
    Before...
    > test1 running
    > test2 running
    ...After 
    

    【讨论】:

    • 这是一个好的开始。但是,您可能可以将这 2 个规则结合起来,只使用一个 RetryRule,它会在循环中不断重新评估,直到测试通过或最大重试次数。
    • 但是操作希望 @ before 和 @ after 在每次重试之前和之后执行,用于测试集。
    【解决方案3】:

    您使用@After@Afterclass 属性来装饰测试名称

    @After
    @Test
    @Category(SmokeTests.class)
    public void testProductPageOnly() throws TimeoutException {
       //Some tests here.
    }
    
    @Afterclass
    public static void SomeTest {
       //Some test here.
    }
    

    需要注意的是,@Afterclass始终运行;即使您使用的是引发异常的@Beforeclass

    【讨论】:

    • 这不会总是运行两次测试,而不仅仅是第一次失败?
    • @JohnB - 如其所写,是的。但这对 OP 来说是有好处的,因为他的测试似乎由于连接问题而失败,这在本质上可能是暂时的。
    • 但是如果第一次通过,第二次失败,就会报失败,他得到失败的可能性是两倍。
    • 其实重点是只在第一次执行失败的情况下再运行两次测试。
    • 你可以随时使用junit的assume有条件地运行测试
    【解决方案4】:

    希望这可以解决问题:

    1) 测试类应该继承自junit.framework.TestCase

    2) 使用类似这样的方式运行测试

    YourTestClass testClass = new YourTestClass();
    TestResult result = testClass.run();
    Enumeration<TestFailure> failures = result.failures();
    if (result.failureCount() != 0)
    {
       TestFailure fail = failes.nextElement();
       junit.framework.Test test = fail.failedTest();
       test.run( result );
    }
    

    最后result 将包含测试运行的最后结果,因此在分析失败的测试后,您可以再次运行它。

    【讨论】:

    • 除非您无法升级到 JUnit 4.x 或需要 Java 5 之前的兼容性,否则您不应扩展/使用任何 junit.framework.*
    猜你喜欢
    • 2011-12-05
    • 1970-01-01
    • 2012-01-07
    • 2018-07-26
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多