How to intercept SLF4J (with logback) logging via a JUnit test?
The Slf4j API doesn't provide such a way but Logback provides a simple solution.
You can use ListAppender
: a whitebox logback appender where log entries are added in a public List
field that we could use to make our assertions.
Here is a simple example.
Foo class :
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 class :
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<ILoggingEvent> 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<ILoggingEvent> 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()); }}
You can also use Matcher/assertion libraries as AssertJ or Hamcrest.
With AssertJ it would be :
import org.assertj.core.api.Assertions;Assertions.assertThat(listAppender.list) .extracting(ILoggingEvent::getFormattedMessage, ILoggingEvent::getLevel) .containsExactly(Tuple.tuple("start", Level.INFO), Tuple.tuple("finish", Level.INFO));
You can create a custom appender
public class TestAppender extends AppenderBase<LoggingEvent> { static List<LoggingEvent> events = new ArrayList<>(); @Override protected void append(LoggingEvent e) { events.add(e); }}
and configure logback-test.xml to use it. Now we can check logging events from our test:
@Testpublic void test() { ... Assert.assertEquals(1, TestAppender.events.size()); ...}
NOTE: Use ILoggingEvent
if you do not get any output - see the comment section for the reasoning.
You can use slf4j-test from http://projects.lidalia.org.uk/slf4j-test/.It replaces the entire logback slf4j implementation by it's own slf4j api implementation for tests and provides an api to assert against logging events.
example:
<build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <configuration> <classpathDependencyExcludes> <classpathDependencyExcludes>ch.qos.logback:logback-classic</classpathDependencyExcludes> </classpathDependencyExcludes> </configuration> </plugin> </plugins></build>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(); }}