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

后端 未结 8 896
天涯浪人
天涯浪人 2020-12-04 10:52

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...?

相关标签:
8条回答
  • 2020-12-04 11:50

    A simple solution could be to mock the appender with Mockito (for example)

    MyClass.java

    @Slf4j
    class MyClass {
        public void doSomething() {
            log.info("I'm on it!");
        }
    }
    

    MyClassTest.java

    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.Matchers.containsString;
    import static org.hamcrest.Matchers.is;
    import static org.mockito.Mockito.verify;         
    
    @RunWith(MockitoJUnitRunner.class)
    public class MyClassTest {    
    
        @Mock private Appender<ILoggingEvent> mockAppender;
        private MyClass sut = new MyClass();    
    
        @Before
        public void setUp() {
            Logger logger = (Logger) LoggerFactory.getLogger(MyClass.class.getName());
            logger.addAppender(mockAppender);
        }
    
        @Test
        public void shouldLogInCaseOfError() {
            sut.doSomething();
    
            verify(mockAppender).doAppend(ArgumentMatchers.argThat(argument -> {
                assertThat(argument.getMessage(), containsString("I'm on it!"));
                assertThat(argument.getLevel(), is(Level.INFO));
                return true;
            }));
    
        }
    
    }
    

    NOTE: I'm using assertion rather than returning false as it makes code and (possible) error easier to read, but it won't work if you have multiple verifications. In that case you need to return boolean indicating if the value is as expected.

    0 讨论(0)
  • 2020-12-04 11:53

    I would recommend a simple, reusable spy implementation that can be included in a test as JUnit rule:

    public final class LogSpy extends ExternalResource {
    
        private Logger logger;
        private ListAppender<ILoggingEvent> appender;
    
        @Override
        protected void before() {
            appender = new ListAppender<>();
            logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); // cast from facade (SLF4J) to implementation class (logback)
            logger.addAppender(appender);
            appender.start();
        }
    
        @Override
        protected void after() {
            logger.detachAppender(appender);
        }
    
        public List<ILoggingEvent> getEvents() {
            if (appender == null) {
                throw new UnexpectedTestError("LogSpy needs to be annotated with @Rule");
            }
            return appender.list;
        }
    }
    

    In your test, you'd activate the spy in the following way:

    @Rule
    public LogSpy log = new LogSpy();
    

    Call log.getEvents() (or other, custom methods) to check the logged events.

    0 讨论(0)
提交回复
热议问题