What is the best way to unit-test SLF4J log messages?

Create a test rule:

    import ch.qos.logback.classic.Logger;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import ch.qos.logback.core.read.ListAppender;
    import org.junit.rules.TestRule;
    import org.junit.runner.Description;
    import org.junit.runners.model.Statement;
    import org.slf4j.LoggerFactory;
    
    import java.util.List;
    import java.util.stream.Collectors;
    
    public class LoggerRule implements TestRule {
    
      private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
      private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    
      @Override
      public Statement apply(Statement base, Description description) {
        return new Statement() {
          @Override
          public void evaluate() throws Throwable {
            setup();
            base.evaluate();
            teardown();
          }
        };
      }
    
      private void setup() {
        logger.addAppender(listAppender);
        listAppender.start();
      }
    
      private void teardown() {
        listAppender.stop();
        listAppender.list.clear();
        logger.detachAppender(listAppender);
      }
    
      public List<String> getMessages() {
        return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList());
      }
    
      public List<String> getFormattedMessages() {
        return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList());
      }
    
    }

Then use it:

    @Rule
    public final LoggerRule loggerRule = new LoggerRule();
    
    @Test
    public void yourTest() {
        // ...
        assertThat(loggerRule.getFormattedMessages().size()).isEqualTo(2);
    }

—– JUnit 5 with Extension Oct 2021 —–

LogCapture:

public class LogCapture {

  private ListAppender<ILoggingEvent> listAppender = new ListAppender<>();

  LogCapture() {
  }

  public String getFirstFormattedMessage() {
    return getFormattedMessageAt(0);
  }

  public String getLastFormattedMessage() {
    return getFormattedMessageAt(listAppender.list.size() - 1);
  }

  public String getFormattedMessageAt(int index) {
    return getLoggingEventAt(index).getFormattedMessage();
  }

  public LoggingEvent getLoggingEvent() {
    return getLoggingEventAt(0);
  }

  public LoggingEvent getLoggingEventAt(int index) {
    return (LoggingEvent) listAppender.list.get(index);
  }

  public List<LoggingEvent> getLoggingEvents() {
    return listAppender.list.stream().map(e -> (LoggingEvent) e).collect(Collectors.toList());
  }

  public void setLogFilter(Level logLevel) {
    listAppender.clearAllFilters();
    listAppender.addFilter(buildLevelFilter(logLevel));
  }

  public void clear() {
    listAppender.list.clear();
  }

  void start() {
    setLogFilter(Level.INFO);
    listAppender.start();
  }

  void stop() {
    if (listAppender == null) {
      return;
    }

    listAppender.stop();
    listAppender.list.clear();
    listAppender = null;
  }

  ListAppender<ILoggingEvent> getListAppender() {
    return listAppender;
  }

  private Filter<ILoggingEvent> buildLevelFilter(Level logLevel) {
    LevelFilter levelFilter = new LevelFilter();
    levelFilter.setLevel(logLevel);
    levelFilter.setOnMismatch(FilterReply.DENY);
    levelFilter.start();

    return levelFilter;
  }

}

LogCaptureExtension:

public class LogCaptureExtension implements ParameterResolver, AfterTestExecutionCallback {

  private Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

  private LogCapture logCapture;

  @Override
  public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
    return parameterContext.getParameter().getType() == LogCapture.class;
  }

  @Override
  public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
    logCapture = new LogCapture();

    setup();

    return logCapture;
  }

  @Override
  public void afterTestExecution(ExtensionContext context) {
    teardown();
  }

  private void setup() {
    logger.addAppender(logCapture.getListAppender());
    logCapture.start();
  }

  private void teardown() {
    if (logCapture == null || logger == null) {
      return;
    }

    logger.detachAndStopAllAppenders();
    logCapture.stop();
  }

}

then use it:

@ExtendWith(LogCaptureExtension.class)
public class SomeTest {

  @Test
  public void sometest(LogCapture logCapture)  {
    // do test here

    assertThat(logCapture.getLoggingEvents()).isEmpty();
  }

  // ...
}

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)