Skip to content

answer-it/mock-slf4j

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

mock-slf4j

##About mock-slf4j This library was born from my own needs. I would like to share it with you hopping that it will help you, too. I have been searching a while for the best approach on how to mock and query static and final loggers. The first thing that can be done is to make use of PowerMock. That powerful indeed library helps you to mock static and final fields. That's easy. The hard work begins after. You need to collect logged data and to query for what you want to test, most commonly for the message and the logging level. The things get more complicated when you want to take into consideration for your test the mapped diagnostic context (MDC), the markers or the message format parameters. More data has to be collected and queried. This is boring and error prone job. Here this library comes into the picture. Its goal is to help you to get rid of the hard work and let you focus on testing the logged data, which should be your main goal.

How to use

Mocking static logger

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class ClassWithStaticLogger 
{
	private static final Logger LOGGER = LoggerFactory.getLogger(ClassWithStaticLogger.class);
	...
}
public class MockSlf4jTest 
{
	...
	
	@Test
	public void test_mockStatic()
	{
		Logger mockedLogger = MockSlf4j.mockStatic(ClassWithStaticLogger.class, "LOGGER");
		...
	}
}

Mocking non-static logger

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

final class ClassWithNonStaticLogger 
{
	private final Logger logger = LoggerFactory.getLogger(ClassWithNonStaticLogger.class);
	...
}
public class MockSlf4jTest 
{
	...

	@Test
	public void test_mock()
	{
		ClassWithNonStaticLogger instance = new ClassWithNonStaticLogger();
		Logger mockedLogger = MockSlf4j.mock(instance, "logger");
		...
	}
}

Testing logged data

Test log entries count

import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.junit.Assert.assertThat;

//Asserts that exactly 5 entries have been logged 
assertThat(mockedLogger, hasEntriesCount(5));

Test message content

import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasAtLeastOneEntryThat;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveMessage;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.junit.Assert.assertThat;

....
//Asserts that at least one entry with 'a message' message has been logged 
assertThat(logger, hasAtLeastOneEntry(that(haveMessage(equalTo("Message")))));

//Asserts that at least one message that starts with 'Hello' has been logged
assertThat(logger, hasAtLeastOneEntry(that(haveMessage(that(startsWith("Hello"))))));

Test message format parameters

import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveNoParam;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveParams;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.containParams;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.junit.Assert.assertThat;

assertThat(mockedLogger, hasEntriesCount(1, that(haveParams())));
assertThat(mockedLogger, hasEntriesCount(1, that(haveNoParam())));

//Asserts that there is exactly one log entry with format parameters array that includes p1 and p2
assertThat(mockedLogger, hasEntriesCount(1, that(containParams(p1, p2))));

//Asserts that there is exactly one log entry with format parameters array equals to {p1,p2,p3}
assertThat(mockedLogger, hasEntriesCount(1, that(haveParams(p1, p2, p3))));

Test logging level

import static org.answerit.mock.slf4j.LoggingEventLevelMatcher.haveLevel;
import static org.answerit.mock.slf4j.LoggingLevel.DEBUG;
import static org.answerit.mock.slf4j.LoggingLevel.ERROR;
import static org.answerit.mock.slf4j.LoggingLevel.INFO;
import static org.answerit.mock.slf4j.LoggingLevel.TRACE;
import static org.answerit.mock.slf4j.LoggingLevel.WARN;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.junit.Assert.assertThat;

assertThat(mockedLogger, hasEntriesCount(6, that(haveLevel(TRACE))));
assertThat(mockedLogger, hasEntriesCount(5, that(haveLevel(DEBUG))));
assertThat(mockedLogger, hasEntriesCount(4, that(haveLevel(INFO))));
assertThat(mockedLogger, hasEntriesCount(3, that(haveLevel(WARN))));
assertThat(mockedLogger, hasEntriesCount(2, that(haveLevel(ERROR))));

Test mapped diagnostic context (MDC)

import static org.answerit.mock.slf4j.MockSlf4jMatchers.containMDC;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveMDC;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.hamcrest.CoreMatchers.anyOf;
import static org.junit.Assert.assertThat;

assertThat(logger, hasEntriesCount(2, that(containMDC("key1", "val1"))));
assertThat(logger, hasEntriesCount(1, that(haveMDC("key1","val1","key2","val2","key3","val3"))));

Test markers

import static org.answerit.mock.slf4j.MockSlf4jMatchers.containAtLeastOneMarker;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.containMarker;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.containNoMarker;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.junit.Assert.assertThat;

assertThat(logger, hasEntriesCount(1, that(containNoMarker())));
assertThat(logger, hasEntriesCount(2, that(containMarker("MARKER_NAME"))));

Test cause

import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasEntriesCount;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveCause;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.haveCauseMessage;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.that;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;

//Checks the cause recursively
assertThat(logger, hasEntriesCount(1, that(haveCause(instanceOf(NullPointerException.class)))));

//Checks recursively for a cause with the given message
assertThat(logger, hasEntriesCount(1, that(haveCauseMessage(that(containsString("Invalid parameter"))))));

Test thread

import static org.answerit.mock.slf4j.MockSlf4jMatchers.hasAtLeastOneEntry;
import static org.answerit.mock.slf4j.MockSlf4jMatchers.fromThreadWithName;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.junit.Assert.assertThat;

assertThat(logger, hasAtLeastOneEntry(fromThreadWithName(equalTo("main"))));

More complex test

Map<String, String> contextMap = new HashMap<String, String>();
contextMap.put("authenticationToken", null);
contextMap.put("ipAddress", "192.168.254.254");
contextMap.put("method", "GET");
contextMap.put("request", "/users");
MDC.setContextMap(contextMap);

Marker securityAlertMarker = MarkerFactory.getDetachedMarker("SECURITY_ALERT");

logger.error(securityAlertMarker, "User not currently logged in");

assertThat(logger, hasAtLeastOneEntry(that(allOf(
	haveLevel(LoggingLevel.ERROR),
	containMDC("authenticationToken", nullValue()),
	containMDC("request", anything()),
	containMarker("SECURITY_ALERT"),
	haveMessage(allOf(
		containsString("not"),
		containsString("logged")
		)))
	)));