Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Is it possible to somehow intercept the logging (SLF4J + logback) and get an InputStream (or something else that is readable) via a JUnit test case...?

like image 486
carlspring Avatar asked Mar 16 '15 12:03

carlspring


People also ask

Does SLF4J support Logback?

SLF4J supports popular logging frameworks, namely log4j, java. util. logging, Simple logging and NOP. The logback project supports SLF4J natively.

How do I test a logger message in Junit?

@StefanoL: If a logger is defined with private static final Logger LOGGER = Logger. getLogger(someString); , you can still access it from unit tests with Logger. getLogger(someString); , modify it and add handlers (as in the accepted answer).

How do I cover log debug in Junit?

how to change the log level for certain junit test methods. The way to go is to use a custom junit MethodRule that accesses the loggers and re-configures the log level per package. With below classes, you can achieve this.

Which logger is SLF4J using?

The SLF4J or the Simple Logging Facade for Java is an abstraction layer for various Java logging frameworks, like Log4j 2 or Logback.


2 Answers

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)); 
like image 56
davidxxx Avatar answered Sep 30 '22 08:09

davidxxx


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:

@Test public 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.

like image 25
Evgeniy Dorofeev Avatar answered Sep 30 '22 10:09

Evgeniy Dorofeev