How to intercept SLF4J (with logback) logging via a JUnit test? How to intercept SLF4J (with logback) logging via a JUnit test? java java

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