1
votes

In a unit test in a spring-boot environment with slf4j and logback, I want to make sure LOGGER.isTraceEnabled() returns true for a particular test class only.

The reason for that is, that sometimes we have slow and non trivial code guarded with if (LOGGER.isTraceEnabled()) {...}. In a unit test we want to make sure it does not break the application if we switch on trace.

Code under test:

public class ClassUnderTest{

    private static final Logger LOGGER = LoggerFactory.getLogger(ClassUnderTest.class);

    public void doSomething(Calendar gegenwart) {
        if (LOGGER.isTraceEnabled()) {
           // non trivial code
        }
    }
}

Test code:

public class ClassUnderTestUnitTest{

    @Test
    public void findeSnapshotMitLueke() {
        ClassUnderTest toTestInstance = new ClassUnderTest ();

        // I want to make sure trace logging is enabled, when
        // this method is executed.
        // Trace logging should not be enabled outside this
        // test class.
        toTestInstance.doSomething(Calendar.getInstance());
    }

}
2
What exactly do you want to test? That the non trivial code works, when trace is enabled OR that trace is enabled for only this specific class? - Chris311
That the non trivial code works. - BetaRide

2 Answers

1
votes

In case you don't want to use a framework like powermock you can do the following trick:

public class ClassUnderTest {

    private Supplier<Logger> loggerSupplier = () -> getLogger(ClassUnderTest.class);

    public void doSomething(Calendar gegenwart) {
        if (loggerSupplier.get().isTraceEnabled()) {
           // non trivial code
        }
    }
}

Now you are able to mock the logger:

public class ClassUnderTestUnitTest{

    @Mock
    private Supplier<Mock> loggerSupplier;
    @Mock
    private Logger logger;

    @Test
    public void findeSnapshotMitLueke() {
        ClassUnderTest toTestInstance = new ClassUnderTest ();

        when(loggerSupplier.get()).thenReturn(logger);
        when(logger.isTraceEnabled()).thenReturn(true)

        toTestInstance.doSomething(Calendar.getInstance());

        // verifyLogger();
    }

}
2
votes

You can easily create a static mock of LoggerFactory using PowerMock and cause it to return a regular mock of Logger (using EasyMock). Then you simply define mock implementation of Logger.isTraceEnabled() and Logger.trace().

Off the top of my head:


@PrepareForTest({ LoggerFactory.class })
@RunWith(PowerMockRunner.class) // assuming JUnit...
public class ClassUnderTestUnitTest{

    @Test
    public void findeSnapshotMitLueke() {

        Logger mockLogger = EasyMock.createMock(Logger.class);
        EasyMock.expect(mockLogger.isTraceEnabled()).andReturn(true);
        EasyMock.expect(mockLogger.trace(any()));
        EasyMock.expectLastCall().anyTimes() // as trace is a void method.
        // Repeat for other log methods ...

        PowerMock.mockStatic(LoggerFactory.class);
        EasyMock.expect(LoggerFactory.getLogger(ClassUnderTest.class)
               .andReturn(mockLogger);

        PowerMock.replay(mockLogger, LoggerFactory.class);

        ClassUnderTest toTestInstance = new ClassUnderTest ();

        // I want to make sure trace logging is enabled, when
        // this method is executed.
        // Trace logging should not be enabled outside this
        // test class.
        toTestInstance.doSomething(Calendar.getInstance());

        // After the operation if needed you can verify that the mocked methods were called.
        
        PowerMock.verify(mockLogger).times(...);
        PowerMock.verifyStatic(LoggerFactory.class).times(...);
    }

}