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

时间:2011-09-21 16:28:18

标签: java exception logging junit 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;
        }
    }
}

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


,我现在在做什么

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

3 个答案:

答案 0 :(得分: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
    }
}

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
    }

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

    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();
                }
            }
        };
    }

编辑:以下作品:

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

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

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)
...

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

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

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

这样,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);
   }
}

写日志

starting outer rule
starting middle rule
starting inner rule
finished inner rule
finished middle rule
finished outer rule

答案 1 :(得分:2)

看看盒子外面......你用什么来运行测试?大多数运行测试的环境(例如,Ant,Jenkins,Maven等)都有使用testrunner输出XML文件的方法,支持将套件中的XML文件聚合成综合报告。

答案 2 :(得分:0)

这看起来很容易,我认为我错了,你问的是不同的东西,但也许我可以提供帮助:

JUnit 4.X

@Test(预期= Exception.class)

如果在测试中抛出异常,

将通过测试,或者由Junit框架捕获的消息失败