Comprehensive test suite and testing utilities for Apache Log4j core logging implementation
—
Custom Hamcrest matchers for file-based assertions and Map testing, providing fluent assertion APIs for common testing scenarios in Log4j applications.
Hamcrest matchers for File objects, enabling fluent file-based assertions in tests.
/**
* Hamcrest matchers for File objects
*/
public final class FileMatchers {
/**
* Matches if file exists
* @return Matcher that succeeds if file exists
*/
public static Matcher<File> exists();
/**
* Matches file length against provided matcher
* @param lengthMatcher Matcher for file length in bytes
* @return Matcher that checks file length
*/
public static Matcher<File> hasLength(Matcher<Long> lengthMatcher);
/**
* Matches if file is empty (length = 0)
* @return Matcher that succeeds if file is empty
*/
public static Matcher<File> isEmpty();
/**
* Matches file last modified time against provided matcher
* @param lastModifiedMatcher Matcher for last modified timestamp
* @return Matcher that checks last modified time
*/
public static Matcher<File> lastModified(Matcher<Long> lastModifiedMatcher);
/**
* Matches if directory contains files (not empty directory)
* @return Matcher that succeeds if directory has files
*/
public static Matcher<File> hasFiles();
/**
* Matches file name against provided matcher
* @param nameMatcher Matcher for file name string
* @return Matcher that checks file name
*/
public static Matcher<File> hasName(Matcher<String> nameMatcher);
}File Matcher Usage Examples:
import org.apache.logging.log4j.core.test.hamcrest.FileMatchers;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
public class FileMatcherTest {
@Test
public void testLogFileCreation() {
File logFile = new File("test.log");
// Verify log file exists after logging
Logger logger = LogManager.getLogger();
logger.info("Test message");
assertThat(logFile, FileMatchers.exists());
assertThat(logFile, FileMatchers.hasLength(greaterThan(0L)));
assertThat(logFile, not(FileMatchers.isEmpty()));
}
@Test
public void testLogFileRotation() throws InterruptedException {
File logFile = new File("rolling.log");
long beforeTime = System.currentTimeMillis();
Logger logger = LogManager.getLogger();
logger.info("Message before rotation");
Thread.sleep(10); // Ensure time difference
// Trigger rotation
logger.info("Message after rotation");
assertThat(logFile, FileMatchers.exists());
assertThat(logFile, FileMatchers.lastModified(greaterThan(beforeTime)));
}
@Test
public void testLogDirectory() {
File logDir = new File("logs");
// After logging to directory
Logger logger = LogManager.getLogger();
logger.info("Directory test message");
assertThat(logDir, FileMatchers.exists());
assertThat(logDir, FileMatchers.hasFiles());
}
@Test
public void testLogFileName() {
File logFile = new File("application-2023-12-01.log");
assertThat(logFile, FileMatchers.hasName(startsWith("application")));
assertThat(logFile, FileMatchers.hasName(endsWith(".log")));
assertThat(logFile, FileMatchers.hasName(containsString("2023")));
}
@Test
public void testEmptyLogFile() {
File emptyLog = new File("empty.log");
emptyLog.createNewFile(); // Create empty file
assertThat(emptyLog, FileMatchers.exists());
assertThat(emptyLog, FileMatchers.isEmpty());
assertThat(emptyLog, FileMatchers.hasLength(equalTo(0L)));
}
}Hamcrest matchers for Map objects, useful for testing structured logging data and MDC contexts.
/**
* Hamcrest matchers for Map objects
*/
public class MapMatchers {
/**
* Matches if map contains specified key
* @param key The key to check for
* @return Matcher that succeeds if map contains key
*/
public static <K> Matcher<Map<? super K, ?>> hasKey(K key);
/**
* Matches if map contains specified value
* @param value The value to check for
* @return Matcher that succeeds if map contains value
*/
public static <V> Matcher<Map<?, ? super V>> hasValue(V value);
/**
* Matches if map contains specified key-value pair
* @param key The key to check for
* @param value The expected value for the key
* @return Matcher that succeeds if map contains key-value pair
*/
public static <K, V> Matcher<Map<? super K, ? super V>> hasEntry(K key, V value);
/**
* Matches if map is empty
* @return Matcher that succeeds if map is empty
*/
public static Matcher<Map<?, ?>> isEmpty();
/**
* Matches map size against provided matcher
* @param sizeMatcher Matcher for map size
* @return Matcher that checks map size
*/
public static Matcher<Map<?, ?>> hasSize(Matcher<Integer> sizeMatcher);
}Map Matcher Usage Examples:
import org.apache.logging.log4j.core.test.hamcrest.MapMatchers;
import static org.hamcrest.MatcherAssert.assertThat;
public class MapMatcherTest {
@Test
public void testMDCContext() {
ThreadContext.put("userId", "12345");
ThreadContext.put("sessionId", "abcdef");
ThreadContext.put("requestId", "req-001");
Map<String, String> mdc = ThreadContext.getContext();
assertThat(mdc, MapMatchers.hasKey("userId"));
assertThat(mdc, MapMatchers.hasValue("12345"));
assertThat(mdc, MapMatchers.hasEntry("sessionId", "abcdef"));
assertThat(mdc, MapMatchers.hasSize(equalTo(3)));
assertThat(mdc, not(MapMatchers.isEmpty()));
}
@Test
public void testLogEventProperties() {
// Capture log event with properties
ListAppender appender = getListAppender("test");
Logger logger = LogManager.getLogger();
logger.info("Test message with properties");
LogEvent event = appender.getEvents().get(0);
Map<String, String> contextMap = event.getContextData().toMap();
assertThat(contextMap, MapMatchers.hasKey("threadName"));
assertThat(contextMap, not(MapMatchers.isEmpty()));
}
}Description utilities for enhanced Hamcrest matcher descriptions and error messages.
/**
* Description utilities for Hamcrest matchers
*/
public class Descriptors {
/**
* Creates description for file properties
* @param file The file to describe
* @return String description of file properties
*/
public static String describeFile(File file);
/**
* Creates description for map contents
* @param map The map to describe
* @return String description of map contents
*/
public static String describeMap(Map<?, ?> map);
/**
* Creates description for collection contents
* @param collection The collection to describe
* @return String description of collection
*/
public static String describeCollection(Collection<?> collection);
/**
* Formats description with proper indentation
* @param description The description to format
* @param indentLevel Indentation level
* @return Formatted description string
*/
public static String formatDescription(String description, int indentLevel);
}// Basic file existence and content checks
assertThat(logFile, FileMatchers.exists());
assertThat(logFile, FileMatchers.hasLength(greaterThan(100L)));
assertThat(logFile, not(FileMatchers.isEmpty()));
// File naming patterns
assertThat(logFile, FileMatchers.hasName(startsWith("app")));
assertThat(logFile, FileMatchers.hasName(endsWith(".log")));
// Directory and file system checks
assertThat(logDir, FileMatchers.hasFiles());
assertThat(backupFile, FileMatchers.lastModified(greaterThan(startTime)));// Combining multiple matchers
assertThat(logFile, allOf(
FileMatchers.exists(),
FileMatchers.hasLength(greaterThan(0L)),
FileMatchers.hasName(containsString("application"))
));
// Either/or scenarios
assertThat(configFile, either(
FileMatchers.hasName(endsWith(".xml"))
).or(
FileMatchers.hasName(endsWith(".json"))
));// Test MDC content
Map<String, String> mdc = ThreadContext.getContext();
assertThat(mdc, allOf(
MapMatchers.hasKey("userId"),
MapMatchers.hasEntry("action", "login"),
MapMatchers.hasSize(greaterThan(2))
));
// Test log event context
LogEvent event = appender.getEvents().get(0);
Map<String, String> contextData = event.getContextData().toMap();
assertThat(contextData, MapMatchers.hasEntry("traceId", expectedTraceId));// Combining with standard Hamcrest matchers
assertThat(logFile, both(
FileMatchers.exists()
).and(
FileMatchers.hasLength(between(100L, 1000L))
));
// Negation patterns
assertThat(tempLogFile, not(anyOf(
FileMatchers.isEmpty(),
FileMatchers.hasName(containsString("production"))
)));Install with Tessl CLI
npx tessl i tessl/maven-org-apache-logging-log4j--log4j-core-test