如何通过JUnit测试拦截SLF4J日志记录?

是否有可能通过JUnit测试用例以某种方式拦截日志记录(SLF4J + logback)并获取InputStream (或其他可读的东西)?

您可以创建自定义appender

 public class TestAppender extends AppenderBase { static List events = new ArrayList<>(); @Override protected void append(LoggingEvent e) { events.add(e); } } 

并配置logback-test.xml以使用它。 现在我们可以从测试中检查记录事件:

 @Test public void test() { ... Assert.assertEquals(1, TestAppender.events.size()); ... } 

您可以使用http://projects.lidalia.org.uk/slf4j-test/中的 slf4j-test。 它通过它自己的slf4j api实现来替换整个logback slf4j实现,并为测试提供了一个api来对报告事件进行断言。

例:

    maven-surefire-plugin   ch.qos.logback:logback-classic      public class Slf4jUser { private static final Logger logger = LoggerFactory.getLogger(Slf4jUser.class); public void aMethodThatLogs() { logger.info("Hello World!"); } } public class Slf4jUserTest { Slf4jUser slf4jUser = new Slf4jUser(); TestLogger logger = TestLoggerFactory.getTestLogger(Slf4jUser.class); @Test public void aMethodThatLogsLogsAsExpected() { slf4jUser.aMethodThatLogs(); assertThat(logger.getLoggingEvents(), is(asList(info("Hello World!")))); } @After public void clearLoggers() { TestLoggerFactory.clear(); } } 

测试日志行时遇到问题: LOGGER.error(message,exception)

http://projects.lidalia.org.uk/slf4j-test/中描述的解决方案也尝试在exception上断言,并且重新创建堆栈跟踪并不容易(在我看来毫无价值)。

我这样解决了:

 import org.junit.Test; import org.slf4j.Logger; import uk.org.lidalia.slf4jext.LoggerFactory; import uk.org.lidalia.slf4jtest.TestLogger; import uk.org.lidalia.slf4jtest.TestLoggerFactory; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; import static uk.org.lidalia.slf4jext.Level.ERROR; import static uk.org.lidalia.slf4jext.Level.INFO; public class Slf4jLoggerTest { private static final Logger LOGGER = LoggerFactory.getLogger(Slf4jLoggerTest.class); private void methodUnderTestInSomeClassInProductionCode() { LOGGER.info("info message"); LOGGER.error("error message"); LOGGER.error("error message with exception", new RuntimeException("this part is not tested")); } private static final TestLogger TEST_LOGGER = TestLoggerFactory.getTestLogger(Slf4jLoggerTest.class); @Test public void testForMethod() throws Exception { // when methodUnderTestInSomeClassInProductionCode(); // then assertThat(TEST_LOGGER.getLoggingEvents()).extracting("level", "message").contains( tuple(INFO, "info message"), tuple(ERROR, "error message"), tuple(ERROR, "error message with exception") ); } } 

这也有不依赖于Hamcrest匹配器库的优势。

Slf4j API不提供这种方式,但Logback提供了一个简单的解决方案。

您可以使用ListAppender :whitebox logback appender,其中日志条目添加到我们可用于进行断言的public List字段中。

这是一个简单的例子。

Foo类:

 import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Foo { static final Logger LOGGER = LoggerFactory.getLogger(Foo .class); public void doThat() { logger.info("start"); //... logger.info("finish"); } } 

FooTest类:

 import org.slf4j.LoggerFactory; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; public class FooTest { @Test void doThat() throws Exception { // get Logback Logger Logger fooLogger = (Logger) LoggerFactory.getLogger(Foo.class); // create and start a ListAppender ListAppender listAppender = new ListAppender<>(); listAppender.start(); // add the appender to the logger fooLogger.addAppender(listAppender); // call method under test Foo foo = new Foo(); foo.doThat(); // JUnit assertions List logsList = listAppender.list; assertEquals("start", logsList.get(0) .getMessage()); assertEquals(Level.INFO, logsList.get(0) .getLevel()); assertEquals("finish", logsList.get(1) .getMessage()); assertEquals(Level.INFO, logsList.get(1) .getLevel()); } } 

您还可以使用Matcher /断言库作为AssertJ或Hamcrest。

使用AssertJ,它将是:

 import org.assertj.core.api.Assertions; Assertions.assertThat(listAppender.list) .extracting(ILoggingEvent::getMessage, ILoggingEvent::getLevel) .containsExactly(Tuple.tuple("start", Level.INFO), Tuple.tuple("finish", Level.INFO));