java123999
java123999

Reputation: 7394

Testing that a Logback Log statment was called using Junit?

Below is a method that I am attempting to write Junit test for:

Method I wish to test:

   //logger declared outside of method in Class
   private static Logger LOGGER = LoggerFactory.getLogger(PersonService.class);

    public void showOutputOfIdentifications(int age) {

        if(age>25){

            LOGGER.info("Over 25");

        }else{

            LOGGER.info("25 or Under");

        }
     }

How can I test to verify that the correct Logback Log statement has been called?

In my tests I have tried to mock the logger and verify that it was called, however this has not worked?

Current Test Attempt:

 @Test
    public void testShowOutputOfIdentifications() throws ParseException{

    int age = 10;

    Logger LOGGER_mock = mock(Logger.class);

    //call method under test
    showOutputOfIdentifications(age);

    verify(LOGGER_mock).info("25 or under");


}

Current failing test output:

Wanted but not invoked:
logger.info(
    "25 or under "
);

Actually, there were zero interactions with this mock.

Upvotes: 8

Views: 18418

Answers (2)

sebdehne
sebdehne

Reputation: 382

You could add your own appender and assert that the log message was written that way, see Programmatically configure LogBack appender.

Something like this:

// create the mock appender
Appender mockedAppender = Mockito.mock(Appender.class);

// inject it
((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).addAppender(mockedAppender);

// run your test
LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME).error("Test msg");

// verify using ArgumentCaptor
ArgumentCaptor<Appender> argumentCaptor = ArgumentCaptor.forClass(Appender.class);
Mockito.verify(mockedAppender).doAppend(argumentCaptor.capture());

// assert against argumentCaptor.getAllValues()
Assert.assertEquals(1, argumentCaptor.getAllValues().size());
Assert.assertEquals("Test msg", ((LoggingEvent)argumentCaptor.getAllValues().get(0)).getMessage());

// remove the mock appender from static context
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).detachAppender(mockedAppender);

Upvotes: 21

Daniel Abbruzzese
Daniel Abbruzzese

Reputation: 76

I wrote this using Groovy and Spock to Mock the appenders.

My logback-test.xml file looks like

<configuration debug="true">
<statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender>

<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%msg%n</pattern>
    </encoder>
</appender>

<logger name="clicks" level="INFO" additivity="false">
    <appender-ref ref="JSON"/>
</logger>
<logger name="com.carsnip" level="DEBUG"/>

<root level="INFO">
    <appender-ref ref="STDOUT"/>
</root>

And my test contains:

class LogTest extends Specification{
    def "should log"() {
      given:
        Logger root = (Logger) 
        LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        Appender rootMockAppender = Mock()
        root.addAppender(rootMockAppender);
        Logger click = (Logger) LoggerFactory.getLogger("clicks");
        Appender clickMockAppender = Mock()
        click.addAppender(clickMockAppender);

      when:
        click.info("HEYYYYY")

     then:
        0 * rootMockAppender.doAppend(_)
        1 * clickMockAppender.doAppend(_)
    }
}

This is written to check the additivity attribute, so that the ROOT logger, which is higher up in the hierarchy does not pick up the logs from our clicks logger which is going to a different location.

Upvotes: 2

Related Questions