javaunit-testingjunitmockitoslf4j

How to mock Logger when created with the @Slf4j annotation?


I have a class that has the @Slf4j annotation.

I try to write a test and mock the Logger, but it does not work.

@RequiredArgsConstructor
@Slf4j
public abstract class ExampleClass {
    
    protected final PropsClass properties;
    
    protected void logInfo(..) {
        log.info(...);
        clearMappedDiagnosticContext();
    }
}

This is how the test looks like:

@RunWith(MockitoJUnitRunner.class)
public class ExampleClassTest {
    
    @Mock
    Logger logger;
    
    @Mock
    PropsClass properties;
    
    @InjectMocks
    ExampleClass exampleClass;
    
    @Test
    public void logSomethingtest() {
        ...
        exampleClass.logInfo(...);
        Mockito.verify(logger).info(marker, "foo bar {}", ...);
    }

This is the error I get:

Wanted but not invoked:
logger.info(
    MY_MARKER,
    "..........",
    "....",
    "....",
    0L
);

Actually, there were zero interactions with this mock.

The question is, how to mock the Logger?


Solution

  • The lombok @Slf4j annotation injects code into your class at compile time. Specifically, it will add the following code to your class:

    private static final org.slf4j.Logger log =
                            org.slf4j.LoggerFactory.getLogger(LogExample.class);
    

    @InjectMocks is telling Mockito to create an instance of your class and inject mocks as its dependencies at runtime.

    The logger is injected at compile time. Dependecies are injected at runtime. That's why your logger is not mocked and cannot be mocked like this. If you look at the injected logger code above, you will understand, that the only way to mock the logger is to mock the LoggerFactory (Mockito can mock static methods since version 3.4, IIRC) and make it return the logger mock.

    NB: Making mocks return mocks is usually a bad idea and should be avoided. @Slf4j is too convenient to not be used. It's a tradeoff.

    NB: If all you want it silencing the logger, then you could also just configure it to shut up in your tests.