简体   繁体   English

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

[英]Best way of logging exceptions when tests fail (e.g. using a junit rule)

When I'm running a complete test suite, it would be helpful if exceptions that caused a test to fail would appear in my (SLF4J-)log. 当我运行一个完整的测试套件时,如果导致测试失败的异常会出现在我的(SLF4J-)日志中,将会很有帮助。 What is the best method to achieve this? 实现这一目标的最佳方法是什么?

What I would like 我想要什么

is a junit4 rule that handles exception logging for me. 是一个junit4规则,为我处理异常日志记录。 The code 代码

@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;
        }
    }
}

of course does not work, since I can only catch exceptions out of a try block. 当然不起作用,因为我只能从try块中捕获异常。 Is there a workaround to somehow achieve this in a similarly simple and general way? 是否有一种解决方法以某种方式以类似的简单和一般方式实现这一目标?


BTW, what I'm doing right now 顺便说一下,我现在在做什么

is logging the exception the moment it is created. 在创建异常时记录异常。 But it would be nicer to log exceptions at the interface between caller and the library, so in my case in the test case. 但是在调用者和库之间的接口上记录异常会更好,所以在我的情况下在测试用例中。 Not logging when the exceptions are created would also guarantee that they don't show up multiple times when the caller decides to log them. 在创建例外时不记录也可以保证在调用者决定记录它们时它们不会多次显示。

You need to extend TestRule, in particular the apply(). 您需要扩展TestRule,特别是apply()。 For an example, have a look at org.junit.rules.ExternalResource & org.junit.rules.TemporaryFolder. 例如,看看org.junit.rules.ExternalResource&org.junit.rules.TemporaryFolder。

ExternalResource looks like this: 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 then extends this and implements before() and after(). 然后,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
    }

So the before gets called before the testMethod, and the after is called in the finally, but you can catch and log any Exception, like: 所以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();
                }
            }
        };
    }

EDIT: The following works: 编辑:以下工作:

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

The test passes and you get the following output: 测试通过,您将获得以下输出:

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

The order that the rules are applied is ExpectedException which calls ExceptionLoggingRule which calls the testMe method. 应用规则的顺序是ExpectedException,它调用ExceptionLoggingRule来调用testMe方法。 The ExceptionLoggingRule catches the Exception, logs it and rethrows it, and it is then processed by ExpectedException. ExceptionLoggingRule捕获Exception,记录并重新抛出它,然后由ExpectedException处理。

If you want to log only unexpected exceptions, you just switch the declaration order of the rules: 如果只想记录意外的异常,只需切换规则的声明顺序:

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

That way, expectedException is applied first (ie nested in exceptionLoggingRule), and only rethrows exceptions that are not expected. 这样,expectException首先应用(即嵌套在exceptionLoggingRule中),并且仅重新抛出不期望的异常。 Furthermore, if some exception was expected and none occured, expectedException will throw an AssertionError which will also get logged. 此外,如果预期有一些异常且没有发生异常,那么expectedException将抛出一个AssertionError,它也将被记录。

This evaluation order isn't guaranteed, but it is quite unlikely to vary unless you're playing with very different JVMs, or inheriting between Test classes. 此评估顺序无法保证,但除非您使用非常不同的JVM或在Test类之间继承,否则不太可能发生变化。

If the evaluation order is important, then you can always pass one rule to the other for evaluation. 如果评估顺序很重要,那么您始终可以将一个规则传递给另一个规则以进行评估。

EDIT: With the recently released Junit 4.10, you can use @RuleChain to chain rules correctly: 编辑:使用最近发布的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);
   }
}

writes the log 写日志

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

Look outside the box... What are you using to run the tests? 看看盒子外面......你用什么来运行测试? Most environments for running tests (eg, Ant, Jenkins, Maven, etc.) have means for using a testrunner that outputs an XML file, with support for aggregating the XML files from a suite into a comprehensive report. 大多数运行测试的环境(例如,Ant,Jenkins,Maven等)具有使用输出XML文件的testrunner的方法,支持将套件中的XML文件聚合为综合报告。

This seems so easy that I think I am wrong and you are asking something different, but maybe I can help: 这似乎很容易,我认为我错了,你问的是不同的东西,但也许我可以帮忙:

JUnit 4.X JUnit 4.X

@Test(expected=Exception.class) @Test(预期= Exception.class)

is going to pass the test if the exception is thrown in the test, or fail with a message captured by the Junit framework 如果在测试中抛出异常,则将通过测试,或者由Junit框架捕获的消息失败

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 有没有办法将长时间运行(例如压力测试)分开,以便它们在Maven 2中默认不运行? - Is there a way to separate long running (e.g. stress tests) out so they're not run by default in Maven 2? Facebook应用程序的测试帐户(例如,JUnit测试手册) - Test accounts for Facebook application (e.g. for manual of JUnit testing) Java:以水平方向显示多个缓冲图像的最佳方法(例如,胶片卷轴) - Java: Best way to display multiple buffered images in a horizontal orientation(e.g. film reel) 在Guice中两次绑定相同类型(例如包装器)以进行依赖项注入的最佳方法是什么? - What is the best way to bind the same type twice (e.g. a wrapper) for dependency injection in Guice? 从Scala(和Java)访问DRb对象(例如Ruby Queue)的最佳方法是什么? - What's the best way of accessing a DRb object (e.g. Ruby Queue) from Scala (and Java)? 终端操作(例如forEach)是否可以重新抛出已检查的异常? - Can a terminal operation (e.g. forEach) rethrow checked exceptions? 使用Proguard完全删除未使用的字段引用(例如,日志记录) - Fully remove unused field references (e.g. logging) with Proguard 有关使用的JUnit jar版本的Ant JUnit任务报告(例如Junit 3、3.8、4) - Ant JUnit task report on the version of the JUnit jar used (e.g. Junit 3, 3.8, 4) JUnit 测试在创建新文件时失败 - JUnit tests fail when creating new Files 在Azure中使用Java以任何方式打开自定义入站防火墙端口,例如8077 - Open custom inbound firewall ports e.g. 8077 by any way using java in azure
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM