当@BeforeClass失败时,如何执行自定义代码

时间:2016-07-13 18:12:44

标签: java junit

我使用junit与docker运行一些集成测试。

我有一个自定义的TestRule,可以在某个测试失败时从容器中复制日志:

public static class CopyLogsOnTestFailure implements TestRule {
    @Override
    public Statement apply(Statement base, Description description) {
        return statement(base, description);
    }

    private Statement statement(final Statement base, Description description) {
        return new Statement() {

            final String pathToFolder = "/tmp/logs";

            @Override
            public void evaluate() throws Throwable {
                try {
                    base.evaluate();
                } catch (Throwable caughtThrowable) {
                    logger.error("Copyings logs on failure!");
                    copyLogs(pathToFolder);
                    throw caughtThrowable;
                }
            }
        };
    }

现在,如果测试在@BeforeClass中失败,则不会复制日志,因为TestRule对@BeforeClass或@AfterClass方法没有好处。

我试图制作一个类似的@ClassRule来测试它,但是当@FeforeClass中的testfails运行时,它运行@AfterClass,然后运行规则。显然,在我的情况下,@ AfterClass删除容器。

知道如何解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

JUnit将执行代码的一般顺序如下(blog post with sample code):

@ClassRule
  @BeforeClass
    @Rule
      @Before
        @Test
        method1()
      @After
      @Before
        @Test2
        method2()
      @After
    terminate rule
  @AfterClass`
terminate class rule 

我已经对执行进行了修改,因此如果在什么级别上引发异常,您可以更轻松地查看将执行哪些清理代码。

在第一个使用简单JUnit @Rule带注释规则的情况下,异常发生在规则执行之前,这解释了@AfterClass代码的执行。

在第二种情况下,首先执行@ClassRule代码,然后执行@BeforeClass中产生异常的代码。 Junit现在将尝试执行已执行方法的清理代码。首先执行@AfterClass,然后执行@ClassRule清理。

您可以将@BeforeClass@AfterClass重构为另一个规则和chain them together with the other rule,但您可能会注意到相同的行为,因为外部规则在内部规则之前执行,如果内部失败,执行内部规则的清理,然后清除外部规则。如果外部规则失败,则内部规则根本不执行。

基本上你想要的是,如果我理解你的意图正确,就是为@AfterClass清理创建一个外部规则,它正在做这样的清理工作:

public class CleanUpRule implements TestRule {

    @Override
    public Statement apply(final Statement base, final Description description) {
        try {
            base.evaluate();
        } finally {
            // @AfterClass cleanup code here
        }
    }
}

@BeforeClass的代码可以放入其他规则

public class InitializationRule implements TestRule {

    @Override
    public Statement apply(final Statement base, final Description description) {
        // @BeforeClass logic here
        base.evaluate();
    }
}

然后创建一个像这样的链规则:

@ClassRule
public static TestRule chain = RuleChain.outer(new CleanUpRule())
                                        .around(new CopyLogsOnTestFailure())
                                        .around(new InitializationRule());

JUnit将首先调用外部规则(CleanUpRule)并调用base.evaluate()方法,该方法将调用下一个规则CopyLogsOnTestFailure,该规则也只是将调用传播到下一个规则{ {1}}。如果此规则失败,则由于缺少异常处理程序,异常将传播到先前规则InitializationRule,异常处理程序将在该规则中启动并将日志复制到其他位置。当重新抛出异常时,异常现在由第一个规则CopyLosOnTestFailure处理,它只执行finally块,因此执行前CleanUpRule清理。