0
# Hamcrest Matchers
1
2
Custom Hamcrest matchers for file-based assertions and Map testing, providing fluent assertion APIs for common testing scenarios in Log4j applications.
3
4
## Capabilities
5
6
### File Matchers
7
8
Hamcrest matchers for File objects, enabling fluent file-based assertions in tests.
9
10
```java { .api }
11
/**
12
* Hamcrest matchers for File objects
13
*/
14
public final class FileMatchers {
15
16
/**
17
* Matches if file exists
18
* @return Matcher that succeeds if file exists
19
*/
20
public static Matcher<File> exists();
21
22
/**
23
* Matches file length against provided matcher
24
* @param lengthMatcher Matcher for file length in bytes
25
* @return Matcher that checks file length
26
*/
27
public static Matcher<File> hasLength(Matcher<Long> lengthMatcher);
28
29
/**
30
* Matches if file is empty (length = 0)
31
* @return Matcher that succeeds if file is empty
32
*/
33
public static Matcher<File> isEmpty();
34
35
/**
36
* Matches file last modified time against provided matcher
37
* @param lastModifiedMatcher Matcher for last modified timestamp
38
* @return Matcher that checks last modified time
39
*/
40
public static Matcher<File> lastModified(Matcher<Long> lastModifiedMatcher);
41
42
/**
43
* Matches if directory contains files (not empty directory)
44
* @return Matcher that succeeds if directory has files
45
*/
46
public static Matcher<File> hasFiles();
47
48
/**
49
* Matches file name against provided matcher
50
* @param nameMatcher Matcher for file name string
51
* @return Matcher that checks file name
52
*/
53
public static Matcher<File> hasName(Matcher<String> nameMatcher);
54
}
55
```
56
57
**File Matcher Usage Examples:**
58
59
```java
60
import org.apache.logging.log4j.core.test.hamcrest.FileMatchers;
61
import static org.hamcrest.MatcherAssert.assertThat;
62
import static org.hamcrest.Matchers.*;
63
64
public class FileMatcherTest {
65
66
@Test
67
public void testLogFileCreation() {
68
File logFile = new File("test.log");
69
70
// Verify log file exists after logging
71
Logger logger = LogManager.getLogger();
72
logger.info("Test message");
73
74
assertThat(logFile, FileMatchers.exists());
75
assertThat(logFile, FileMatchers.hasLength(greaterThan(0L)));
76
assertThat(logFile, not(FileMatchers.isEmpty()));
77
}
78
79
@Test
80
public void testLogFileRotation() throws InterruptedException {
81
File logFile = new File("rolling.log");
82
long beforeTime = System.currentTimeMillis();
83
84
Logger logger = LogManager.getLogger();
85
logger.info("Message before rotation");
86
87
Thread.sleep(10); // Ensure time difference
88
89
// Trigger rotation
90
logger.info("Message after rotation");
91
92
assertThat(logFile, FileMatchers.exists());
93
assertThat(logFile, FileMatchers.lastModified(greaterThan(beforeTime)));
94
}
95
96
@Test
97
public void testLogDirectory() {
98
File logDir = new File("logs");
99
100
// After logging to directory
101
Logger logger = LogManager.getLogger();
102
logger.info("Directory test message");
103
104
assertThat(logDir, FileMatchers.exists());
105
assertThat(logDir, FileMatchers.hasFiles());
106
}
107
108
@Test
109
public void testLogFileName() {
110
File logFile = new File("application-2023-12-01.log");
111
112
assertThat(logFile, FileMatchers.hasName(startsWith("application")));
113
assertThat(logFile, FileMatchers.hasName(endsWith(".log")));
114
assertThat(logFile, FileMatchers.hasName(containsString("2023")));
115
}
116
117
@Test
118
public void testEmptyLogFile() {
119
File emptyLog = new File("empty.log");
120
emptyLog.createNewFile(); // Create empty file
121
122
assertThat(emptyLog, FileMatchers.exists());
123
assertThat(emptyLog, FileMatchers.isEmpty());
124
assertThat(emptyLog, FileMatchers.hasLength(equalTo(0L)));
125
}
126
}
127
```
128
129
### Map Matchers
130
131
Hamcrest matchers for Map objects, useful for testing structured logging data and MDC contexts.
132
133
```java { .api }
134
/**
135
* Hamcrest matchers for Map objects
136
*/
137
public class MapMatchers {
138
139
/**
140
* Matches if map contains specified key
141
* @param key The key to check for
142
* @return Matcher that succeeds if map contains key
143
*/
144
public static <K> Matcher<Map<? super K, ?>> hasKey(K key);
145
146
/**
147
* Matches if map contains specified value
148
* @param value The value to check for
149
* @return Matcher that succeeds if map contains value
150
*/
151
public static <V> Matcher<Map<?, ? super V>> hasValue(V value);
152
153
/**
154
* Matches if map contains specified key-value pair
155
* @param key The key to check for
156
* @param value The expected value for the key
157
* @return Matcher that succeeds if map contains key-value pair
158
*/
159
public static <K, V> Matcher<Map<? super K, ? super V>> hasEntry(K key, V value);
160
161
/**
162
* Matches if map is empty
163
* @return Matcher that succeeds if map is empty
164
*/
165
public static Matcher<Map<?, ?>> isEmpty();
166
167
/**
168
* Matches map size against provided matcher
169
* @param sizeMatcher Matcher for map size
170
* @return Matcher that checks map size
171
*/
172
public static Matcher<Map<?, ?>> hasSize(Matcher<Integer> sizeMatcher);
173
}
174
```
175
176
**Map Matcher Usage Examples:**
177
178
```java
179
import org.apache.logging.log4j.core.test.hamcrest.MapMatchers;
180
import static org.hamcrest.MatcherAssert.assertThat;
181
182
public class MapMatcherTest {
183
184
@Test
185
public void testMDCContext() {
186
ThreadContext.put("userId", "12345");
187
ThreadContext.put("sessionId", "abcdef");
188
ThreadContext.put("requestId", "req-001");
189
190
Map<String, String> mdc = ThreadContext.getContext();
191
192
assertThat(mdc, MapMatchers.hasKey("userId"));
193
assertThat(mdc, MapMatchers.hasValue("12345"));
194
assertThat(mdc, MapMatchers.hasEntry("sessionId", "abcdef"));
195
assertThat(mdc, MapMatchers.hasSize(equalTo(3)));
196
assertThat(mdc, not(MapMatchers.isEmpty()));
197
}
198
199
@Test
200
public void testLogEventProperties() {
201
// Capture log event with properties
202
ListAppender appender = getListAppender("test");
203
Logger logger = LogManager.getLogger();
204
205
logger.info("Test message with properties");
206
207
LogEvent event = appender.getEvents().get(0);
208
Map<String, String> contextMap = event.getContextData().toMap();
209
210
assertThat(contextMap, MapMatchers.hasKey("threadName"));
211
assertThat(contextMap, not(MapMatchers.isEmpty()));
212
}
213
}
214
```
215
216
### Descriptors
217
218
Description utilities for enhanced Hamcrest matcher descriptions and error messages.
219
220
```java { .api }
221
/**
222
* Description utilities for Hamcrest matchers
223
*/
224
public class Descriptors {
225
226
/**
227
* Creates description for file properties
228
* @param file The file to describe
229
* @return String description of file properties
230
*/
231
public static String describeFile(File file);
232
233
/**
234
* Creates description for map contents
235
* @param map The map to describe
236
* @return String description of map contents
237
*/
238
public static String describeMap(Map<?, ?> map);
239
240
/**
241
* Creates description for collection contents
242
* @param collection The collection to describe
243
* @return String description of collection
244
*/
245
public static String describeCollection(Collection<?> collection);
246
247
/**
248
* Formats description with proper indentation
249
* @param description The description to format
250
* @param indentLevel Indentation level
251
* @return Formatted description string
252
*/
253
public static String formatDescription(String description, int indentLevel);
254
}
255
```
256
257
## Usage Patterns
258
259
### File Assertion Patterns
260
261
```java
262
// Basic file existence and content checks
263
assertThat(logFile, FileMatchers.exists());
264
assertThat(logFile, FileMatchers.hasLength(greaterThan(100L)));
265
assertThat(logFile, not(FileMatchers.isEmpty()));
266
267
// File naming patterns
268
assertThat(logFile, FileMatchers.hasName(startsWith("app")));
269
assertThat(logFile, FileMatchers.hasName(endsWith(".log")));
270
271
// Directory and file system checks
272
assertThat(logDir, FileMatchers.hasFiles());
273
assertThat(backupFile, FileMatchers.lastModified(greaterThan(startTime)));
274
```
275
276
### Combined Matcher Patterns
277
278
```java
279
// Combining multiple matchers
280
assertThat(logFile, allOf(
281
FileMatchers.exists(),
282
FileMatchers.hasLength(greaterThan(0L)),
283
FileMatchers.hasName(containsString("application"))
284
));
285
286
// Either/or scenarios
287
assertThat(configFile, either(
288
FileMatchers.hasName(endsWith(".xml"))
289
).or(
290
FileMatchers.hasName(endsWith(".json"))
291
));
292
```
293
294
### MDC and Context Testing
295
296
```java
297
// Test MDC content
298
Map<String, String> mdc = ThreadContext.getContext();
299
assertThat(mdc, allOf(
300
MapMatchers.hasKey("userId"),
301
MapMatchers.hasEntry("action", "login"),
302
MapMatchers.hasSize(greaterThan(2))
303
));
304
305
// Test log event context
306
LogEvent event = appender.getEvents().get(0);
307
Map<String, String> contextData = event.getContextData().toMap();
308
assertThat(contextData, MapMatchers.hasEntry("traceId", expectedTraceId));
309
```
310
311
### Custom Matcher Integration
312
313
```java
314
// Combining with standard Hamcrest matchers
315
assertThat(logFile, both(
316
FileMatchers.exists()
317
).and(
318
FileMatchers.hasLength(between(100L, 1000L))
319
));
320
321
// Negation patterns
322
assertThat(tempLogFile, not(anyOf(
323
FileMatchers.isEmpty(),
324
FileMatchers.hasName(containsString("production"))
325
)));
326
```