提问者:小点点

使用Junit重新运行失败的测试


我正在尝试改进现有的自动化Selenium测试系统。我的目标是重复由于连接问题而失败的测试。我已经找到并尝试遵循这个线程如何立即重新运行失败的JUnit测试?这显示了自己非常有用。

在我的例子中,套件是由类组成的,所以我试着用@类规则代替@规则,以便每次尝试都重复@之前和@之后部分。对不起,我的无知,但是我应该把这个规则放在哪里?在我的套件类中?还是在代表测试的类中?


共3个答案

匿名用户

我是如何立即重新运行失败的JUnit测试的原始回答者?

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

所以你现在的行为是这样的:

@Before
@Retry
test code
@Retry
@After

但是您可以将您的@之前@之后作为规则实现-有一个规则ExternalResource就是这样做的。您将实现@之前@之后作为规则:

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

    public void after() {
        // code that was in @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,您不再需要ExternalResourceRetryLaw上的@Law注释,而是在RuleChain上。

匿名用户

这是我根据问题中提到的解决方案。

它是@Law、FailedLaw和@ClassLaw、RetryLaw的组合

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

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

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

匿名用户

您可以使用@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