I'm using slf4j and I want to unit test my code to make sure that warn/error log messages are generated under certain conditions. I'd rather these be strict unit tests, so I'd prefer not to have to pull up logging configuration from a file in order to test that the log messages are generated. The mocking framework I'm using is Mockito.
Yes, we should test logging when the logging is doing something that is required. For example, you have hooks in some external application that scans the log for certain events. In that case you certainly want to ensure the logging is done.
@StefanoL: If a logger is defined with private static final Logger LOGGER = Logger. getLogger(someString); , you can still access it from unit tests with Logger. getLogger(someString); , modify it and add handlers (as in the accepted answer).
SLF4J supports popular logging frameworks, namely log4j, java. util. logging, Simple logging and NOP.
slf4j. simpleLogger. defaultLogLevel - Default log level for all instances of SimpleLogger. Must be one of ("trace", "debug", "info", "warn", "error" or "off").
For testing slf4j without relying on a specific implementation (such as log4j), you can provide your own slf4j logging implementation as described in this SLF4J FAQ. Your implementation can record the messages that were logged and then be interrogated by your unit tests for validation.
The slf4j-test package does exactly this. It's an in-memory slf4j logging implementation that provides methods for retrieving logged messages.
Create a test rule:
import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.slf4j.LoggerFactory; import java.util.List; import java.util.stream.Collectors; public class LoggerRule implements TestRule { private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>(); private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); @Override public Statement apply(Statement base, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { setup(); base.evaluate(); teardown(); } }; } private void setup() { logger.addAppender(listAppender); listAppender.start(); } private void teardown() { listAppender.stop(); listAppender.list.clear(); logger.detachAppender(listAppender); } public List<String> getMessages() { return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList()); } public List<String> getFormattedMessages() { return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList()); } }
Then use it:
@Rule public final LoggerRule loggerRule = new LoggerRule(); @Test public void yourTest() { // ... assertThat(loggerRule.getFormattedMessages().size()).isEqualTo(2); }
----- JUnit 5 with Extension Oct 2021 -----
LogCapture:
public class LogCapture { private ListAppender<ILoggingEvent> listAppender = new ListAppender<>(); LogCapture() { } public String getFirstFormattedMessage() { return getFormattedMessageAt(0); } public String getLastFormattedMessage() { return getFormattedMessageAt(listAppender.list.size() - 1); } public String getFormattedMessageAt(int index) { return getLoggingEventAt(index).getFormattedMessage(); } public LoggingEvent getLoggingEvent() { return getLoggingEventAt(0); } public LoggingEvent getLoggingEventAt(int index) { return (LoggingEvent) listAppender.list.get(index); } public List<LoggingEvent> getLoggingEvents() { return listAppender.list.stream().map(e -> (LoggingEvent) e).collect(Collectors.toList()); } public void setLogFilter(Level logLevel) { listAppender.clearAllFilters(); listAppender.addFilter(buildLevelFilter(logLevel)); } public void clear() { listAppender.list.clear(); } void start() { setLogFilter(Level.INFO); listAppender.start(); } void stop() { if (listAppender == null) { return; } listAppender.stop(); listAppender.list.clear(); listAppender = null; } ListAppender<ILoggingEvent> getListAppender() { return listAppender; } private Filter<ILoggingEvent> buildLevelFilter(Level logLevel) { LevelFilter levelFilter = new LevelFilter(); levelFilter.setLevel(logLevel); levelFilter.setOnMismatch(FilterReply.DENY); levelFilter.start(); return levelFilter; } }
LogCaptureExtension:
public class LogCaptureExtension implements ParameterResolver, AfterTestExecutionCallback { private Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); private LogCapture logCapture; @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return parameterContext.getParameter().getType() == LogCapture.class; } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { logCapture = new LogCapture(); setup(); return logCapture; } @Override public void afterTestExecution(ExtensionContext context) { teardown(); } private void setup() { logger.addAppender(logCapture.getListAppender()); logCapture.start(); } private void teardown() { if (logCapture == null || logger == null) { return; } logger.detachAndStopAllAppenders(); logCapture.stop(); } }
then use it:
@ExtendWith(LogCaptureExtension.class) public class SomeTest { @Test public void sometest(LogCapture logCapture) { // do test here assertThat(logCapture.getLoggingEvents()).isEmpty(); } // ... }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With