6

当我运行一个完整的测试套件时,如果导致测试失败的异常出现在我的 (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 块中捕获异常。是否有一种解决方法可以以同样简单和通用的方式以某种方式实现这一目标?


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

正在记录异常创建的那一刻。但是在调用者和库之间的接口处记录异常会更好,所以在我的例子中是测试用例。在创建异常时不记录也可以保证在调用者决定记录它们时它们不会多次出现。

4

3 回答 3

14

您需要扩展 TestRule,尤其是 apply()。例如,查看 org.junit.rules.ExternalResource 和 org.junit.rules.TemporaryFolder。

外部资源如下所示:

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 中被调用,但是你可以捕获并记录任何异常,比如:

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

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

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

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

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

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

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

编辑:使用最近发布的 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
于 2011-09-21T17:30:09.420 回答
2

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

于 2011-09-21T17:30:00.713 回答
0

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

JUnit 4.X

@Test(expected=Exception.class)

如果在测试中抛出异常,将通过测试,或者失败并显示由 Junit 框架捕获的消息

于 2011-10-05T21:53:20.033 回答