Issue
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.
Answered By - EricSchaefer
Answer Checked By - Mildred Charles (JavaFixing Admin)