0
# Matchers
1
2
JUnit 4 integrates with Hamcrest matchers to provide expressive, readable assertions. Matchers allow you to write assertions in a more natural, English-like syntax and provide better error messages when assertions fail.
3
4
## Capabilities
5
6
### Matcher Assertions
7
8
Use Hamcrest matchers with JUnit assertions for expressive test verification.
9
10
**Note:** As of JUnit 4.13, the `assertThat` methods in `org.junit.Assert` are deprecated. Users should migrate to using Hamcrest's `MatcherAssert.assertThat()` directly, or use third-party assertion libraries like AssertJ or Google Truth.
11
12
**Import Consideration:** When using Hamcrest matchers, avoid using both `import static org.hamcrest.CoreMatchers.*` and `import static org.hamcrest.Matchers.*` together, as many methods exist in both classes and will cause ambiguous reference errors. Use only one wildcard import (typically `org.hamcrest.Matchers.*` which includes all matchers).
13
14
```java { .api }
15
/**
16
* Assert using Hamcrest matcher
17
* @param actual - Value to test
18
* @param matcher - Hamcrest matcher
19
* @deprecated Use {@link org.hamcrest.MatcherAssert#assertThat(Object, Matcher)} instead
20
*/
21
@Deprecated
22
public static <T> void assertThat(T actual, Matcher<? super T> matcher);
23
24
/**
25
* Assert using Hamcrest matcher with custom message
26
* @param reason - Message to display on failure
27
* @param actual - Value to test
28
* @param matcher - Hamcrest matcher
29
* @deprecated Use {@link org.hamcrest.MatcherAssert#assertThat(String, Object, Matcher)} instead
30
*/
31
@Deprecated
32
public static <T> void assertThat(String reason, T actual, Matcher<? super T> matcher);
33
```
34
35
**Usage Examples:**
36
37
```java
38
import org.junit.Test;
39
import static org.junit.Assert.assertThat;
40
import static org.hamcrest.CoreMatchers.*;
41
42
public class MatcherTest {
43
@Test
44
public void testWithMatchers() {
45
// Equality
46
assertThat(5, is(5));
47
assertThat("hello", equalTo("hello"));
48
49
// Negation
50
assertThat(5, not(3));
51
assertThat("hello", not("world"));
52
53
// Null checks
54
assertThat(null, nullValue());
55
assertThat("text", notNullValue());
56
57
// Type checks
58
assertThat("text", instanceOf(String.class));
59
assertThat(123, isA(Integer.class));
60
61
// String matchers
62
assertThat("hello world", containsString("world"));
63
assertThat("hello", startsWith("hel"));
64
assertThat("hello", endsWith("lo"));
65
66
// With custom message
67
assertThat("Value should be positive", 5, greaterThan(0));
68
}
69
70
@Test
71
public void testLogicalMatchers() {
72
// Any of (OR)
73
assertThat(5, either(is(5)).or(is(6)));
74
assertThat("test", anyOf(is("test"), is("demo"), is("example")));
75
76
// Both (AND)
77
assertThat(5, both(greaterThan(0)).and(lessThan(10)));
78
assertThat("test", allOf(startsWith("te"), endsWith("st")));
79
80
// Not
81
assertThat(5, not(lessThan(0)));
82
}
83
84
@Test
85
public void testCollectionMatchers() {
86
List<String> items = Arrays.asList("apple", "banana", "cherry");
87
88
// Has item
89
assertThat(items, hasItem("banana"));
90
assertThat(items, hasItems("apple", "cherry"));
91
92
// Collection properties
93
assertThat(items, not(empty()));
94
assertThat(items.size(), is(3));
95
96
// Array matchers
97
String[] array = {"a", "b", "c"};
98
assertThat(array, arrayContaining("a", "b", "c"));
99
assertThat(array, arrayContainingInAnyOrder("b", "a", "c"));
100
}
101
}
102
```
103
104
### JUnitMatchers
105
106
JUnit-specific matcher utilities that extend Hamcrest matchers with additional functionality.
107
108
**Note:** Most methods in this class are deprecated. Use the equivalent methods from `org.hamcrest.CoreMatchers` instead. Only `isThrowable` and `isException` are not deprecated.
109
110
```java { .api }
111
/**
112
* JUnit-specific Hamcrest matchers
113
*/
114
public class JUnitMatchers {
115
/**
116
* Matcher for collection containing specific element
117
* @param element - Element to find
118
* @return Matcher that matches collections containing element
119
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItem(Object)}
120
*/
121
@Deprecated
122
public static <T> Matcher<Iterable<? super T>> hasItem(T element);
123
124
/**
125
* Matcher for collection containing element matching the given matcher
126
* @param elementMatcher - Matcher for the element
127
* @return Matcher that matches collections containing matching element
128
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItem(Matcher)}
129
*/
130
@Deprecated
131
public static <T> Matcher<Iterable<? super T>> hasItem(Matcher<? super T> elementMatcher);
132
133
/**
134
* Matcher for collection containing all specified elements
135
* @param elements - Elements to find
136
* @return Matcher that matches collections containing all elements
137
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItems(Object...)}
138
*/
139
@Deprecated
140
public static <T> Matcher<Iterable<T>> hasItems(T... elements);
141
142
/**
143
* Matcher for collection containing elements matching all given matchers
144
* @param elementMatchers - Matchers for elements
145
* @return Matcher that matches collections with matching elements
146
* @deprecated use {@link org.hamcrest.CoreMatchers#hasItems(Matcher...)}
147
*/
148
@Deprecated
149
public static <T> Matcher<Iterable<T>> hasItems(Matcher<? super T>... elementMatchers);
150
151
/**
152
* Matcher for collection where every element matches the given matcher
153
* @param elementMatcher - Matcher that each element must match
154
* @return Matcher that matches collections where all elements match
155
* @deprecated use {@link org.hamcrest.CoreMatchers#everyItem(Matcher)}
156
*/
157
@Deprecated
158
public static <T> Matcher<Iterable<T>> everyItem(Matcher<T> elementMatcher);
159
160
/**
161
* Matcher for string containing substring
162
* @param substring - Substring to find
163
* @return Matcher that matches strings containing substring
164
* @deprecated use {@link org.hamcrest.CoreMatchers#containsString(String)}
165
*/
166
@Deprecated
167
public static Matcher<String> containsString(String substring);
168
169
/**
170
* Combine two matchers with AND
171
* @param matcher - First matcher
172
* @return CombinableBothMatcher for chaining
173
* @deprecated use {@link org.hamcrest.CoreMatchers#both(Matcher)}
174
*/
175
@Deprecated
176
public static <T> CombinableBothMatcher<T> both(Matcher<? super T> matcher);
177
178
/**
179
* Combine two matchers with OR
180
* @param matcher - First matcher
181
* @return CombinableEitherMatcher for chaining
182
* @deprecated use {@link org.hamcrest.CoreMatchers#either(Matcher)}
183
*/
184
@Deprecated
185
public static <T> CombinableEitherMatcher<T> either(Matcher<? super T> matcher);
186
187
/**
188
* Matcher for throwable with matching cause, appending stacktrace on mismatch
189
* @param throwableMatcher - Matcher for the throwable
190
* @return Matcher for Throwable
191
*/
192
public static <T extends Throwable> Matcher<T> isThrowable(Matcher<T> throwableMatcher);
193
194
/**
195
* Matcher for exception with matching cause, appending stacktrace on mismatch
196
* @param exceptionMatcher - Matcher for the exception
197
* @return Matcher for Exception
198
*/
199
public static <T extends Exception> Matcher<T> isException(Matcher<T> exceptionMatcher);
200
}
201
```
202
203
**Usage Examples:**
204
205
```java
206
import org.junit.Test;
207
import org.junit.matchers.JUnitMatchers;
208
import static org.junit.Assert.assertThat;
209
import static org.hamcrest.CoreMatchers.*;
210
211
public class JUnitMatchersTest {
212
@Test
213
public void testStringMatcher() {
214
assertThat("hello world", JUnitMatchers.containsString("world"));
215
assertThat("hello world", JUnitMatchers.containsString("hello"));
216
}
217
218
@Test
219
public void testCombinedMatchers() {
220
// Both conditions must be true
221
assertThat(5, JUnitMatchers.both(greaterThan(0)).and(lessThan(10)));
222
223
// Either condition can be true
224
assertThat(5, JUnitMatchers.either(is(5)).or(is(10)));
225
}
226
227
@Test
228
public void testExceptionMatchers() {
229
Exception ex = new RuntimeException("Error", new IOException("IO failed"));
230
231
assertThat(ex, JUnitMatchers.isException(
232
isA(RuntimeException.class)
233
));
234
}
235
}
236
```
237
238
### Common Hamcrest Matchers
239
240
Core matchers from Hamcrest library commonly used with JUnit.
241
242
```java { .api }
243
// Object matchers
244
Matcher<T> equalTo(T operand);
245
Matcher<T> is(T value);
246
Matcher<T> not(T value);
247
Matcher<Object> nullValue();
248
Matcher<Object> notNullValue();
249
Matcher<T> sameInstance(T target);
250
Matcher<T> instanceOf(Class<?> type);
251
Matcher<T> any(Class<T> type);
252
253
// Comparable matchers
254
Matcher<T> greaterThan(T value);
255
Matcher<T> greaterThanOrEqualTo(T value);
256
Matcher<T> lessThan(T value);
257
Matcher<T> lessThanOrEqualTo(T value);
258
259
// String matchers
260
Matcher<String> containsString(String substring);
261
Matcher<String> startsWith(String prefix);
262
Matcher<String> endsWith(String suffix);
263
Matcher<String> equalToIgnoringCase(String expectedString);
264
Matcher<String> equalToIgnoringWhiteSpace(String expectedString);
265
Matcher<String> isEmptyString();
266
Matcher<String> isEmptyOrNullString();
267
268
// Collection matchers
269
Matcher<Collection<T>> hasItem(T item);
270
Matcher<Collection<T>> hasItems(T... items);
271
Matcher<Collection<T>> empty();
272
Matcher<Collection<T>> hasSize(int size);
273
Matcher<Iterable<T>> contains(T... items);
274
Matcher<Iterable<T>> containsInAnyOrder(T... items);
275
276
// Array matchers
277
Matcher<T[]> arrayContaining(T... items);
278
Matcher<T[]> arrayContainingInAnyOrder(T... items);
279
Matcher<T[]> arrayWithSize(int size);
280
Matcher<T[]> emptyArray();
281
282
// Logical matchers
283
Matcher<T> allOf(Matcher<? super T>... matchers);
284
Matcher<T> anyOf(Matcher<? super T>... matchers);
285
Matcher<T> not(Matcher<T> matcher);
286
287
// Number matchers
288
Matcher<Double> closeTo(double operand, double error);
289
```
290
291
**Usage Examples:**
292
293
```java
294
import org.junit.Test;
295
import static org.junit.Assert.assertThat;
296
import static org.hamcrest.Matchers.*;
297
298
public class HamcrestMatchersTest {
299
@Test
300
public void testObjectMatchers() {
301
assertThat(5, equalTo(5));
302
assertThat(5, is(5));
303
assertThat(null, nullValue());
304
assertThat("text", notNullValue());
305
assertThat("text", instanceOf(String.class));
306
assertThat("text", any(String.class));
307
308
String s1 = "hello";
309
String s2 = s1;
310
assertThat(s1, sameInstance(s2));
311
}
312
313
@Test
314
public void testComparableMatchers() {
315
assertThat(10, greaterThan(5));
316
assertThat(10, greaterThanOrEqualTo(10));
317
assertThat(5, lessThan(10));
318
assertThat(5, lessThanOrEqualTo(5));
319
}
320
321
@Test
322
public void testStringMatchers() {
323
assertThat("hello world", containsString("world"));
324
assertThat("hello world", startsWith("hello"));
325
assertThat("hello world", endsWith("world"));
326
assertThat("HELLO", equalToIgnoringCase("hello"));
327
assertThat("hello world", equalToIgnoringWhiteSpace("hello world"));
328
assertThat("", isEmptyString());
329
assertThat(null, isEmptyOrNullString());
330
}
331
332
@Test
333
public void testCollectionMatchers() {
334
List<String> items = Arrays.asList("apple", "banana", "cherry");
335
336
assertThat(items, hasItem("banana"));
337
assertThat(items, hasItems("apple", "cherry"));
338
assertThat(items, hasSize(3));
339
assertThat(items, not(empty()));
340
assertThat(items, contains("apple", "banana", "cherry"));
341
assertThat(items, containsInAnyOrder("cherry", "apple", "banana"));
342
}
343
344
@Test
345
public void testArrayMatchers() {
346
String[] array = {"a", "b", "c"};
347
348
assertThat(array, arrayContaining("a", "b", "c"));
349
assertThat(array, arrayContainingInAnyOrder("c", "a", "b"));
350
assertThat(array, arrayWithSize(3));
351
assertThat(new String[0], emptyArray());
352
}
353
354
@Test
355
public void testLogicalMatchers() {
356
// All conditions must be true
357
assertThat(5, allOf(greaterThan(0), lessThan(10), not(equalTo(7))));
358
359
// At least one condition must be true
360
assertThat(5, anyOf(equalTo(5), equalTo(10), equalTo(15)));
361
362
// Negation
363
assertThat(5, not(equalTo(10)));
364
assertThat(5, not(lessThan(0)));
365
}
366
367
@Test
368
public void testNumberMatchers() {
369
// Floating point comparison with tolerance
370
assertThat(3.14159, closeTo(3.14, 0.01));
371
assertThat(0.33333, closeTo(1.0/3.0, 0.001));
372
}
373
}
374
```
375
376
### Custom Matchers
377
378
Create custom matchers for domain-specific assertions.
379
380
```java { .api }
381
/**
382
* Base class for custom matchers
383
*/
384
public abstract class TypeSafeMatcher<T> extends BaseMatcher<T> {
385
/**
386
* Check if item matches
387
* @param item - Item to check
388
* @return true if matches
389
*/
390
protected abstract boolean matchesSafely(T item);
391
392
/**
393
* Describe the matcher
394
* @param description - Description to append to
395
*/
396
public abstract void describeTo(Description description);
397
398
/**
399
* Describe mismatch
400
* @param item - Item that didn't match
401
* @param mismatchDescription - Description to append to
402
*/
403
protected void describeMismatchSafely(T item, Description mismatchDescription);
404
}
405
```
406
407
**Usage Examples:**
408
409
```java
410
import org.hamcrest.Description;
411
import org.hamcrest.TypeSafeMatcher;
412
import org.hamcrest.Matcher;
413
import org.junit.Test;
414
import static org.junit.Assert.assertThat;
415
416
// Custom matcher for even numbers
417
public class IsEven extends TypeSafeMatcher<Integer> {
418
@Override
419
protected boolean matchesSafely(Integer number) {
420
return number % 2 == 0;
421
}
422
423
@Override
424
public void describeTo(Description description) {
425
description.appendText("an even number");
426
}
427
428
@Override
429
protected void describeMismatchSafely(Integer item, Description mismatchDescription) {
430
mismatchDescription.appendValue(item).appendText(" is odd");
431
}
432
433
public static Matcher<Integer> even() {
434
return new IsEven();
435
}
436
}
437
438
// Custom matcher for valid email
439
public class IsValidEmail extends TypeSafeMatcher<String> {
440
@Override
441
protected boolean matchesSafely(String email) {
442
return email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$");
443
}
444
445
@Override
446
public void describeTo(Description description) {
447
description.appendText("a valid email address");
448
}
449
450
public static Matcher<String> validEmail() {
451
return new IsValidEmail();
452
}
453
}
454
455
// Custom matcher for range
456
public class InRange extends TypeSafeMatcher<Integer> {
457
private final int min;
458
private final int max;
459
460
public InRange(int min, int max) {
461
this.min = min;
462
this.max = max;
463
}
464
465
@Override
466
protected boolean matchesSafely(Integer value) {
467
return value >= min && value <= max;
468
}
469
470
@Override
471
public void describeTo(Description description) {
472
description.appendText("a value between ")
473
.appendValue(min)
474
.appendText(" and ")
475
.appendValue(max);
476
}
477
478
@Override
479
protected void describeMismatchSafely(Integer item, Description mismatchDescription) {
480
mismatchDescription.appendValue(item)
481
.appendText(" is outside range [")
482
.appendValue(min)
483
.appendText(", ")
484
.appendValue(max)
485
.appendText("]");
486
}
487
488
public static Matcher<Integer> inRange(int min, int max) {
489
return new InRange(min, max);
490
}
491
}
492
493
// Usage of custom matchers
494
public class CustomMatcherTest {
495
@Test
496
public void testEvenMatcher() {
497
assertThat(4, IsEven.even());
498
assertThat(2, IsEven.even());
499
// assertThat(3, IsEven.even()); // Fails with "3 is odd"
500
}
501
502
@Test
503
public void testEmailMatcher() {
504
assertThat("user@example.com", IsValidEmail.validEmail());
505
assertThat("test.user@domain.co.uk", IsValidEmail.validEmail());
506
// assertThat("invalid.email", IsValidEmail.validEmail()); // Fails
507
}
508
509
@Test
510
public void testRangeMatcher() {
511
assertThat(5, InRange.inRange(1, 10));
512
assertThat(1, InRange.inRange(1, 10));
513
assertThat(10, InRange.inRange(1, 10));
514
// assertThat(15, InRange.inRange(1, 10)); // Fails with "15 is outside range [1, 10]"
515
}
516
}
517
518
// Complex custom matcher with parameters
519
public class HasProperty<T> extends TypeSafeMatcher<T> {
520
private final String propertyName;
521
private final Matcher<?> valueMatcher;
522
523
public HasProperty(String propertyName, Matcher<?> valueMatcher) {
524
this.propertyName = propertyName;
525
this.valueMatcher = valueMatcher;
526
}
527
528
@Override
529
protected boolean matchesSafely(T obj) {
530
try {
531
String getterName = "get" + capitalize(propertyName);
532
Method getter = obj.getClass().getMethod(getterName);
533
Object value = getter.invoke(obj);
534
return valueMatcher.matches(value);
535
} catch (Exception e) {
536
return false;
537
}
538
}
539
540
@Override
541
public void describeTo(Description description) {
542
description.appendText("has property ")
543
.appendValue(propertyName)
544
.appendText(" matching ")
545
.appendDescriptionOf(valueMatcher);
546
}
547
548
public static <T> Matcher<T> hasProperty(String property, Matcher<?> matcher) {
549
return new HasProperty<>(property, matcher);
550
}
551
552
private String capitalize(String str) {
553
return str.substring(0, 1).toUpperCase() + str.substring(1);
554
}
555
}
556
557
// Usage
558
@Test
559
public void testPropertyMatcher() {
560
User user = new User("Alice", 25);
561
assertThat(user, HasProperty.hasProperty("name", equalTo("Alice")));
562
assertThat(user, HasProperty.hasProperty("age", greaterThan(18)));
563
}
564
```
565
566
### Matcher Composition
567
568
Combine matchers to create complex assertions.
569
570
**Usage Examples:**
571
572
```java
573
import org.junit.Test;
574
import static org.junit.Assert.assertThat;
575
import static org.hamcrest.Matchers.*;
576
577
public class MatcherCompositionTest {
578
@Test
579
public void testComplexComposition() {
580
List<String> items = Arrays.asList("apple", "banana", "apricot", "avocado");
581
582
// All items start with 'a' and have length > 5
583
assertThat(items, everyItem(
584
allOf(
585
startsWith("a"),
586
hasLength(greaterThan(5))
587
)
588
));
589
590
// At least one item equals "banana"
591
assertThat(items, hasItem(equalTo("banana")));
592
593
// Collection has size 4 and contains "apple"
594
assertThat(items, allOf(
595
hasSize(4),
596
hasItem("apple")
597
));
598
}
599
600
@Test
601
public void testNestedMatchers() {
602
Map<String, Integer> scores = new HashMap<>();
603
scores.put("Alice", 95);
604
scores.put("Bob", 87);
605
scores.put("Charlie", 92);
606
607
assertThat(scores, allOf(
608
hasEntry("Alice", 95),
609
hasKey("Bob"),
610
hasValue(greaterThan(85))
611
));
612
}
613
614
@Test
615
public void testObjectPropertyMatchers() {
616
User user = new User("Alice", 25, "alice@example.com");
617
618
assertThat(user, allOf(
619
hasProperty("name", startsWith("A")),
620
hasProperty("age", both(greaterThan(18)).and(lessThan(100))),
621
hasProperty("email", containsString("@"))
622
));
623
}
624
}
625
```
626
627
## Benefits of Using Matchers
628
629
### Improved Readability
630
631
```java
632
// Without matchers
633
assertTrue(result.size() > 0);
634
assertTrue(result.contains("expected"));
635
636
// With matchers - more readable
637
assertThat(result, not(empty()));
638
assertThat(result, hasItem("expected"));
639
```
640
641
### Better Error Messages
642
643
```java
644
// Without matchers
645
// Expected: true but was: false
646
assertTrue(name.startsWith("Mr"));
647
648
// With matchers - more descriptive
649
// Expected: a string starting with "Mr" but: was "Ms Smith"
650
assertThat(name, startsWith("Mr"));
651
```
652
653
### Composable Assertions
654
655
```java
656
// Multiple separate assertions
657
assertTrue(age >= 18);
658
assertTrue(age <= 65);
659
assertNotNull(name);
660
assertTrue(name.length() > 0);
661
662
// Single composed assertion
663
assertThat(age, allOf(greaterThanOrEqualTo(18), lessThanOrEqualTo(65)));
664
assertThat(name, allOf(notNullValue(), not(isEmptyString())));
665
```
666
667
## Types
668
669
```java { .api }
670
/**
671
* Base matcher interface
672
*/
673
public interface Matcher<T> extends SelfDescribing {
674
boolean matches(Object item);
675
void describeMismatch(Object item, Description mismatchDescription);
676
677
@Deprecated
678
void _dont_implement_Matcher___instead_extend_BaseMatcher_();
679
}
680
681
/**
682
* Base class for matchers
683
*/
684
public abstract class BaseMatcher<T> implements Matcher<T> {
685
@Override
686
public void describeMismatch(Object item, Description description);
687
688
@Override
689
public String toString();
690
}
691
692
/**
693
* Matcher description
694
*/
695
public interface Description {
696
Description appendText(String text);
697
Description appendDescriptionOf(SelfDescribing value);
698
Description appendValue(Object value);
699
Description appendList(String start, String separator, String end, Iterable<? extends SelfDescribing> values);
700
}
701
702
/**
703
* Combinable matcher for both/and
704
*/
705
public class CombinableBothMatcher<T> {
706
public CombinableMatcher<T> and(Matcher<? super T> matcher);
707
}
708
709
/**
710
* Combinable matcher for either/or
711
*/
712
public class CombinableEitherMatcher<T> {
713
public CombinableMatcher<T> or(Matcher<? super T> matcher);
714
}
715
716
/**
717
* Combined matcher result
718
*/
719
public class CombinableMatcher<T> extends TypeSafeMatcher<T> {
720
@Override
721
protected boolean matchesSafely(T item);
722
723
@Override
724
public void describeTo(Description description);
725
}
726
```
727