0
# Hamcrest Matchers
1
2
Hamcrest provides a library of matcher objects for building test expressions. Matchers offer readable and composable assertions with detailed failure messages that clearly describe what was expected versus what was actually encountered.
3
4
## Core Matcher Interface
5
6
```scala { .api }
7
trait Matcher[T] {
8
def matches(actual: AnyRef): Boolean
9
def describeTo(description: Description): Unit
10
}
11
12
trait Description {
13
def appendText(text: String): Description
14
def appendDescriptionOf(value: SelfDescribing): Description
15
def appendValue(value: AnyRef): Description
16
}
17
```
18
19
## Matcher Factories
20
21
### CoreMatchers Object
22
23
```scala { .api }
24
object CoreMatchers {
25
def is[T](matcher: Matcher[T]): Matcher[T]
26
def is[T](value: T): Matcher[T]
27
def isA[T](typ: Class[T]): Matcher[T]
28
def any[T](typ: Class[T]): Matcher[T]
29
def instanceOf[T](typ: Class[_]): Matcher[T]
30
def not[T](matcher: Matcher[T]): Matcher[T]
31
def not[T](value: T): Matcher[T]
32
def notNullValue(): Matcher[AnyRef]
33
def notNullValue[T](typ: Class[T]): Matcher[T]
34
def nullValue(): Matcher[AnyRef]
35
def nullValue[T](typ: Class[T]): Matcher[T]
36
def equalTo[T](operand: T): Matcher[T]
37
def equalToObject(operand: AnyRef): Matcher[AnyRef]
38
}
39
```
40
41
## Basic Matchers
42
43
### Identity and Decoration
44
45
```scala { .api }
46
def is[T](matcher: Matcher[T]): Matcher[T]
47
def is[T](value: T): Matcher[T]
48
```
49
50
**Usage:**
51
```scala
52
import org.hamcrest.CoreMatchers._
53
import org.junit.Assert.assertThat
54
55
assertThat(userName, is("admin"))
56
assertThat(userAge, is(greaterThan(18)))
57
assertThat(result, is(notNullValue()))
58
```
59
60
The `is` matcher is purely decorative - it wraps another matcher to improve readability without changing behavior.
61
62
### Type Checking
63
64
```scala { .api }
65
def any[T](typ: Class[T]): Matcher[T]
66
def instanceOf[T](typ: Class[_]): Matcher[T]
67
def isA[T](typ: Class[T]): Matcher[T]
68
```
69
70
**Usage:**
71
```scala
72
assertThat(result, instanceOf(classOf[String]))
73
assertThat(exception, isA(classOf[IllegalArgumentException]))
74
assertThat(value, any(classOf[Number]))
75
```
76
77
- `instanceOf`: Matches if the actual value is an instance of the specified class
78
- `isA`: Alias for `instanceOf` with decorative "is a" reading
79
- `any`: Matches any instance of the specified type
80
81
## Null Value Matchers
82
83
```scala { .api }
84
def nullValue(): Matcher[AnyRef]
85
def nullValue[T](typ: Class[T]): Matcher[T]
86
def notNullValue(): Matcher[AnyRef]
87
def notNullValue[T](typ: Class[T]): Matcher[T]
88
```
89
90
**Usage:**
91
```scala
92
assertThat(optionalValue, nullValue())
93
assertThat(optionalValue, nullValue(classOf[String]))
94
assertThat(requiredField, notNullValue())
95
assertThat(user.getName(), notNullValue(classOf[String]))
96
```
97
98
## Negation Matcher
99
100
```scala { .api }
101
def not[T](matcher: Matcher[T]): Matcher[T]
102
def not[T](value: T): Matcher[T]
103
```
104
105
**Usage:**
106
```scala
107
assertThat(result, not(nullValue()))
108
assertThat(status, not("inactive"))
109
assertThat(age, not(lessThan(0)))
110
```
111
112
The `not` matcher inverts any other matcher, providing clear negation with appropriate error messages.
113
114
## Matcher Integration with Assert
115
116
```scala { .api }
117
def assertThat[T](actual: T, matcher: Matcher[T]): Unit
118
def assertThat[T](reason: String, actual: T, matcher: Matcher[T]): Unit
119
```
120
121
**Usage:**
122
```scala
123
// Basic matcher assertion
124
assertThat(result.size(), is(5))
125
126
// With custom message
127
assertThat("User list should not be empty", users, not(nullValue()))
128
129
// Composed matchers
130
assertThat(user.getStatus(), is(not("inactive")))
131
assertThat(response.getCode(), is(instanceOf(classOf[Integer])))
132
```
133
134
## Advanced Matcher Classes
135
136
### Base Matcher Implementation
137
138
```scala { .api }
139
abstract class BaseMatcher[T] extends Matcher[T] {
140
def describeTo(description: Description): Unit
141
def matches(actual: AnyRef): Boolean
142
}
143
```
144
145
### Diagnosing Matcher
146
147
```scala { .api }
148
abstract class DiagnosingMatcher[T] extends BaseMatcher[T] {
149
def matches(item: AnyRef, mismatchDescription: Description): Boolean
150
final def matches(item: AnyRef): Boolean
151
}
152
```
153
154
DiagnosingMatcher provides enhanced mismatch descriptions by allowing matchers to describe why a match failed.
155
156
## Matcher Assertions vs Regular Assertions
157
158
**Regular Assert:**
159
```scala
160
assertEquals("Expected active user", "active", user.getStatus())
161
// Error: Expected active user expected:<active> but was:<inactive>
162
```
163
164
**Hamcrest Matcher:**
165
```scala
166
assertThat(user.getStatus(), is("active"))
167
// Error: Expected: is "active" but: was "inactive"
168
```
169
170
Hamcrest matchers provide:
171
- More readable error messages
172
- Composable expressions
173
- Better type safety
174
- Consistent failure reporting format
175
176
## Custom Matcher Implementation
177
178
```scala
179
import org.hamcrest.{Description, BaseMatcher}
180
181
class GreaterThanMatcher(threshold: Int) extends BaseMatcher[Int] {
182
def matches(actual: AnyRef): Boolean = {
183
actual.asInstanceOf[Int] > threshold
184
}
185
186
def describeTo(description: Description): Unit = {
187
description.appendText(s"greater than $threshold")
188
}
189
}
190
191
def greaterThan(threshold: Int): Matcher[Int] = new GreaterThanMatcher(threshold)
192
193
// Usage
194
assertThat(score, greaterThan(80))
195
```
196
197
## Error Reporting and Description
198
199
Hamcrest matchers automatically generate descriptive error messages:
200
201
- **Expected**: What the matcher was looking for
202
- **Actual**: What was actually provided
203
- **Mismatch**: Why the match failed (for DiagnosingMatcher)
204
205
This provides clear, consistent test failure reporting across all matcher types.