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. What is the best method to achieve this?
What I would like
is a junit4 rule that handles exception logging for me. 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. 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(). For an example, have a look at org.junit.rules.ExternalResource & org.junit.rules.TemporaryFolder.
ExternalResource looks like this:
TemporaryFolder then extends this and implements before() and after().
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:
EDIT: The following works:
The test passes and you get the following output:
The order that the rules are applied is ExpectedException which calls ExceptionLoggingRule which calls the testMe method. The ExceptionLoggingRule catches the Exception, logs it and rethrows it, and it is then processed by ExpectedException.
If you want to log only unexpected exceptions, you just switch the declaration order of the rules:
That way, expectedException is applied first (i.e. nested in exceptionLoggingRule), and only rethrows exceptions that are not expected. Furthermore, if some exception was expected and none occured, expectedException will throw an AssertionError which will also get logged.
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.
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:
writes the log
Look outside the box... What are you using to run the tests? Most environments for running tests (e.g., 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.
This seems so easy that I think I am wrong and you are asking something different, but maybe I can help:
JUnit 4.X
@Test(expected=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