测试失败时记录异常的最佳方法(例如使用junit规则)

Dav*_*Far 5 java logging junit exception junit-rule

当我运行一个完整的测试套件时,如果导致测试失败的异常会出现在我的(SLF4J-)日志中,将会很有帮助.实现这一目标的最佳方法是什么?

我想要什么

是一个junit4规则,为我处理异常日志记录.代码

@Rule
public TestRule logException = new TestWatcher() {
    @Override
    public void failed(Description d) {
        catch (Exception e) {
            logger.error("Test ({}) failed because of exception {}", d, e);
            throw e;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

当然不起作用,因为我只能从try块中捕获异常.是否有一种解决方法以某种方式以类似的简单和一般方式实现这一目标?


顺便说一下,我现在在做什么

在创建异常时记录异常.但是在调用者和库之间的接口上记录异常会更好,所以在我的情况下在测试用例中.在创建例外时不记录也可以保证在调用者决定记录它们时它们不会多次显示.

Mat*_*ell 13

您需要扩展TestRule,特别是apply().例如,看看org.junit.rules.ExternalResource&org.junit.rules.TemporaryFolder.

ExternalResource看起来像这样:

public abstract class ExternalResource implements TestRule {
    public Statement apply(Statement base, Description description) {
        return statement(base);
    }

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } finally {
                    after();
                }
            }
        };
    }

    /**
     * Override to set up your specific external resource.
     * @throws if setup fails (which will disable {@code after}
     */
    protected void before() throws Throwable {
        // do nothing
    }

    /**
     * Override to tear down your specific external resource.
     */
    protected void after() {
        // do nothing
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,TemporaryFolder扩展它并实现before()和after().

public class TemporaryFolder extends ExternalResource {
    private File folder;

    @Override
    protected void before() throws Throwable {
        // create the folder
    }

    @Override
    protected void after() {
        // delete the folder
    }
Run Code Online (Sandbox Code Playgroud)

所以before在testMethod之前调用,而after在finally中调用,但是你可以捕获并记录任何异常,例如:

    private Statement statement(final Statement base) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                before();
                try {
                    base.evaluate();
                } catch (Exception e) {
                    log.error("caught Exception", e);
                } finally {
                    after();
                }
            }
        };
    }
Run Code Online (Sandbox Code Playgroud)

编辑:以下工作:

public class SoTest {
    public class ExceptionLoggingRule implements TestRule {
        public Statement apply(Statement base, Description description) {
            return statement(base);
        }

        private Statement statement(final Statement base) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    try {
                        base.evaluate();
                    } catch (Exception e) {
                        System.out.println("caught an exception");
                        e.printStackTrace(System.out);
                        throw e;
                    }
                }
            };
        }
    }

    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
    @Rule public ExpectedException expectedException = ExpectedException.none();

    @Test
    public void testMe() throws Exception {
        expectedException.expect(IOException.class);
        throw new IOException("here we are");
    }
}
Run Code Online (Sandbox Code Playgroud)

测试通过,您将获得以下输出:

caught an exception
java.io.IOException: here we are
    at uk.co.farwell.junit.SoTest.testMe(SoTest.java:40)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
...
Run Code Online (Sandbox Code Playgroud)

应用规则的顺序是ExpectedException,它调用ExceptionLoggingRule来调用testMe方法.ExceptionLoggingRule捕获Exception,记录并重新抛出它,然后由ExpectedException处理.

如果只想记录意外的异常,只需切换规则的声明顺序:

    @Rule public ExpectedException expectedException = ExpectedException.none();
    @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
Run Code Online (Sandbox Code Playgroud)

这样,expectException首先应用(即嵌套在exceptionLoggingRule中),并且仅重新抛出不期望的异常.此外,如果预期有一些异常且没有发生异常,那么expectedException将抛出一个AssertionError,它也将被记录.

此评估顺序无法保证,但除非您使用非常不同的JVM或在Test类之间继承,否则不太可能发生变化.

如果评估顺序很重要,那么您始终可以将一个规则传递给另一个规则以进行评估.

编辑:使用最近发布的Junit 4.10,您可以使用@RuleChain正确链接规则:

public static class UseRuleChain {
   @Rule
   public TestRule chain= RuleChain
                          .outerRule(new LoggingRule("outer rule")
                          .around(new LoggingRule("middle rule")
                          .around(new LoggingRule("inner rule");

   @Test
   public void example() {
           assertTrue(true);
   }
}
Run Code Online (Sandbox Code Playgroud)

写日志

starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule
Run Code Online (Sandbox Code Playgroud)