Introduction
Yet another way to testing logging in application which use SLF4J
.
Features
- this binding support for easy create
Mockito
mock forLogger
- call to
Logger
can be delegated to instance ofSimpleLogger
, so we can create standardsimplelogger.properties
- support for testing and mocking
MDC
- light transitive dependencies - only
slf4j-api
andmockito-core
- support testing in parallel in multi thread
- all the Magic are done by
Mockito
plugins, so you don’t need to directly use class from this library - ease use
Contributing and helps
Contributions are welcome!
How to use
First we should add slf4j-mock
binding as dependency to our project.
<dependency> <groupId>org.simplify4u</groupId> <artifactId>slf4j-mock</artifactId> <version>2.1.0<version> <scope>test</scope> </dependency>
We must remember that we can have only one SLF4J
binding on classpath, so look for dependencies like slf4j-simple
, slf4j-log4j12
, slf4j-jdk14
or slf4j-nop
and remove those from project when you want testing logging behavior.
Now we have class which does some logging action
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Example { private static final Logger LOGGER = LoggerFactory.getLogger(Example.class); public void methodWithLogInfo(String message) { LOGGER.info(message); } }
JUnit4
import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; import org.slf4j.Logger; @RunWith(MockitoJUnitRunner.class) public class JUnit4ExampleTest { private static final String INFO_TEST_MESSAGE = "info log test message from JUnit4"; @Mock Logger logger; @InjectMocks Example sut; @Test public void logInfoShouldBeLogged() { // when sut.methodWithLogInfo(INFO_TEST_MESSAGE); // then Mockito.verify(logger).info(INFO_TEST_MESSAGE); Mockito.verifyNoMoreInteractions(logger); } }
JUnit5
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; @ExtendWith(MockitoExtension.class) class JUnit5ExampleTest { private static final String INFO_TEST_MESSAGE = "info log test message from JUnit5"; @Mock Logger logger; @InjectMocks Example sut; @Test void logInfoShouldBeLogged() { // when sut.methodWithLogInfo(INFO_TEST_MESSAGE); // then Mockito.verify(logger).info(INFO_TEST_MESSAGE); Mockito.verifyNoMoreInteractions(logger); } }
MDC support
Example for MDC
testing
... import org.slf4j.spi.MDCAdapter; class MDCTest { @Mock MDCAdapter mdcMock; @Test void testPut() { MDC.put("key", "value"); verify(mdcMock).put("key", "value"); verifyNoMoreInteractions(mdcMock); } @Test void testGet() { when(mdcMock.get("key")).thenReturn("value"); assertThat(MDC.get("key")).isEqualTo("value"); verify(mdcMock).get("key"); verifyNoMoreInteractions(mdcMock); } }
As we see in this case we only need Mock for MDCAdapter
Mock and Spy
In tests, we can use Mockito
annotation to define Logger
-
@Mock
- create Mock for Logger - all call we be done on Mock, so any output will not occur from Logger -
@Spy
- create Spy with SimpleLogger - all call we be done on real methods, so standard behavior for SimpleLogger will be kept
Bind Mock to Logger
One Mock, one Logger in class under test
You simply define:
@Mock Logger logger; @InjectMocks private Example sut;
In this case Example class must have defined one Logger, which be used.
Named Mock for Logger
When we want to use in one test more than one Logger we must create Mock with a name.
@Mock(name = "org.simplify4u.slf4jmock.test.Example1") Logger logger1; @Mock(name = "org.simplify4u.slf4jmock.test.Example2") Logger logger1;
Named Spy for Logger
We also can create Spy for desired Logger, by creating SimpleLogger
@Spy Logger logger1 = new SimpleLogger(Example.class); @Spy Logger logger2 = new SimpleLogger("org.example.Logger");
Manual bind Mock to Logger
When we don’t have possibility to use @ExtendWith(MockitoExtension.class)
, @RunWith(MockitoJUnitRunner.class)
or equivalent method, we can try to start Mockito session manually
class MyTest { @Mock Logger logger; ... MockitoSession mockitoSession; @Before void setup() { mockitoSession = Mockito.mockitoSession().initMocks(this).startMocking(); } @After void cleanup() { mockitoSession.finishMocking(); } @Test void test() { .... } }
or when above is also not possible we must manually bind Mock to Logger.
import org.simplify4u.slf4jmock.LoggerMock; class MyTest { Logger logger; @Before void setup() { logger = Mockito.mock(Logger.class); LoggerMock.setMock(Example.class, logger); } @After void cleanup() { LoggerMock.clearMock(Example.class); } @Test void test() { .... } }
More examples
Yuo can look at source code tests: