测试失败时记录exception的最佳方法(例如使用junit规则)
当我运行一个完整的测试套件时,如果导致测试失败的exception会出现在我的(SLF4J-)日志中,将会很有帮助。 实现这一目标的最佳方法是什么?
我想要什么
是一个junit4规则,为我处理exception日志记录。 代码
@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块中捕获exception。 是否有一种解决方法以某种方式以类似的简单和一般方式实现这一目标?
顺便说一下,我现在在做什么
在创建exception时记录exception。 但是在调用者和库之间的接口上记录exception会更好,所以在我的情况下在测试用例中。 在创建例外时不记录也可以保证在调用者决定记录它们时它们不会多次显示。
您需要扩展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,它调用ExceptionLoggingRule来调用testMe方法。 ExceptionLoggingRule捕获exception,记录并重新抛出它,然后由ExpectedException处理。
如果只想记录意外的exception,只需切换规则的声明顺序:
@Rule public ExpectedException expectedException = ExpectedException.none(); @Rule public ExceptionLoggingRule exceptionLoggingRule = new ExceptionLoggingRule();
这样,expectException首先应用(即嵌套在exceptionLoggingRule中),并且只重新抛出不期望的exception。 此外,如果预期有一些exception并且没有发生exception,那么expectedException将抛出一个AssertionError,它也将被记录。
此评估顺序无法保证,但除非您使用非常不同的JVM或在Test类之间inheritance,否则不太可能发生变化。
如果评估顺序很重要,那么您始终可以将一个规则传递给另一个规则以进行评估。
编辑:使用最近发布的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
看看盒子外面……你用什么来运行测试? 大多数运行测试的环境(例如,Ant,Jenkins,Maven等)都具有使用输出XML文件的testrunner的方法,并支持将套件中的XML文件聚合为综合报告。
这似乎很容易,我认为我错了,你问的是不同的东西,但也许我可以帮忙:
JUnit 4.X
@Test(预期= Exception.class)
如果在测试中抛出exception,则将通过测试,或者由Junit框架捕获的消息失败