0
# Matchers
1
2
Extensive built-in matcher system with type-safe argument validation and support for custom matcher creation.
3
4
## Capabilities
5
6
### Matcher Base Class
7
8
Foundation class for creating custom matchers that integrate with Jest's asymmetric matcher system.
9
10
```typescript { .api }
11
/**
12
* Base class for creating custom matchers
13
*/
14
class Matcher<T> implements MatcherLike<T> {
15
$$typeof: symbol;
16
inverse?: boolean;
17
18
/**
19
* Creates a new matcher instance
20
* @param asymmetricMatch - Function that performs the actual matching logic
21
* @param description - Human-readable description of the matcher
22
*/
23
constructor(
24
readonly asymmetricMatch: MatcherFn<T>,
25
private readonly description: string
26
);
27
28
/** Returns string representation of the matcher */
29
toString(): string;
30
31
/** Returns asymmetric matcher representation */
32
toAsymmetricMatcher(): string;
33
34
/** Returns expected type for Jest error messages */
35
getExpectedType(): string;
36
}
37
38
/**
39
* Function type for matcher predicate functions
40
*/
41
type MatcherFn<T> = (actualValue: T) => boolean;
42
```
43
44
### Built-in Type Matchers
45
46
Matchers for validating basic JavaScript types with full type safety.
47
48
```typescript { .api }
49
/** Matches any value of any type */
50
const any: MatcherCreator<any>;
51
52
/** Matches any boolean value (true or false) */
53
const anyBoolean: MatcherCreator<boolean>;
54
55
/** Matches any string value including empty strings */
56
const anyString: MatcherCreator<string>;
57
58
/** Matches any number that is not NaN */
59
const anyNumber: MatcherCreator<number>;
60
61
/** Matches any function */
62
const anyFunction: MatcherCreator<Function>;
63
64
/** Matches any symbol */
65
const anySymbol: MatcherCreator<Symbol>;
66
67
/** Matches any non-null object */
68
const anyObject: MatcherCreator<any>;
69
```
70
71
**Usage Examples:**
72
73
```typescript
74
import { mock, any, anyString, anyNumber, anyBoolean } from "jest-mock-extended";
75
76
interface Calculator {
77
add: (a: number, b: number) => number;
78
concat: (str1: string, str2: string) => string;
79
compare: (val1: any, val2: any) => boolean;
80
}
81
82
const calc = mock<Calculator>();
83
84
// Type-specific matching
85
calc.add.calledWith(anyNumber(), anyNumber()).mockReturnValue(42);
86
calc.concat.calledWith(anyString(), anyString()).mockReturnValue("result");
87
calc.compare.calledWith(any(), any()).mockReturnValue(anyBoolean());
88
89
// Test calls
90
expect(calc.add(5, 10)).toBe(42);
91
expect(calc.concat("hello", "world")).toBe("result");
92
expect(typeof calc.compare("a", 1)).toBe("boolean");
93
```
94
95
### Collection Matchers
96
97
Matchers for validating arrays, maps, sets, and other collection types.
98
99
```typescript { .api }
100
/** Matches any array */
101
const anyArray: MatcherCreator<any[]>;
102
103
/** Matches any Map instance */
104
const anyMap: MatcherCreator<Map<any, any>>;
105
106
/** Matches any Set instance */
107
const anySet: MatcherCreator<Set<any>>;
108
109
/**
110
* Matches instances of a specific class
111
* @param clazz - Constructor function to match against
112
*/
113
const isA: MatcherCreator<any>;
114
```
115
116
**Usage Examples:**
117
118
```typescript
119
import { mock, anyArray, anyMap, anySet, isA } from "jest-mock-extended";
120
121
class UserModel {
122
constructor(public id: string, public name: string) {}
123
}
124
125
interface DataService {
126
processArray: (items: any[]) => void;
127
processMap: (data: Map<string, any>) => void;
128
processSet: (unique: Set<string>) => void;
129
processUser: (user: UserModel) => void;
130
}
131
132
const service = mock<DataService>();
133
134
// Collection type matching
135
service.processArray.calledWith(anyArray()).mockReturnValue(undefined);
136
service.processMap.calledWith(anyMap()).mockReturnValue(undefined);
137
service.processSet.calledWith(anySet()).mockReturnValue(undefined);
138
139
// Class instance matching
140
service.processUser.calledWith(isA(UserModel)).mockReturnValue(undefined);
141
142
// Test with actual instances
143
service.processArray([1, 2, 3]);
144
service.processMap(new Map([["key", "value"]]));
145
service.processSet(new Set(["a", "b", "c"]));
146
service.processUser(new UserModel("123", "John"));
147
```
148
149
### Content Matchers
150
151
Matchers for validating the contents of collections and objects.
152
153
```typescript { .api }
154
/**
155
* Checks if array includes the specified value
156
* @param arrayVal - Value that should be present in the array
157
*/
158
const arrayIncludes: MatcherCreator<any[], any>;
159
160
/**
161
* Checks if Set contains the specified value
162
* @param setVal - Value that should be present in the Set
163
*/
164
const setHas: MatcherCreator<Set<any>, any>;
165
166
/**
167
* Checks if Map contains the specified key
168
* @param mapKey - Key that should be present in the Map
169
*/
170
const mapHas: MatcherCreator<Map<any, any>, any>;
171
172
/**
173
* Checks if object contains the specified key
174
* @param key - Property name that should exist on the object
175
*/
176
const objectContainsKey: MatcherCreator<any, string>;
177
178
/**
179
* Checks if object contains the specified value
180
* @param value - Value that should be present in object's values
181
*/
182
const objectContainsValue: MatcherCreator<any>;
183
```
184
185
**Usage Examples:**
186
187
```typescript
188
import {
189
mock,
190
arrayIncludes,
191
setHas,
192
mapHas,
193
objectContainsKey,
194
objectContainsValue
195
} from "jest-mock-extended";
196
197
interface SearchService {
198
searchInArray: (items: string[], query: string) => boolean;
199
searchInSet: (items: Set<string>, query: string) => boolean;
200
searchInMap: (data: Map<string, any>, key: string) => boolean;
201
searchInObject: (obj: any, criteria: any) => boolean;
202
}
203
204
const search = mock<SearchService>();
205
206
// Content validation
207
search.searchInArray
208
.calledWith(arrayIncludes("target"), "target")
209
.mockReturnValue(true);
210
211
search.searchInSet
212
.calledWith(setHas("item"), "item")
213
.mockReturnValue(true);
214
215
search.searchInMap
216
.calledWith(mapHas("key"), "key")
217
.mockReturnValue(true);
218
219
search.searchInObject
220
.calledWith(objectContainsKey("name"), "name")
221
.mockReturnValue(true);
222
223
search.searchInObject
224
.calledWith(objectContainsValue("John"), "John")
225
.mockReturnValue(true);
226
227
// Test with matching content
228
expect(search.searchInArray(["a", "target", "c"], "target")).toBe(true);
229
expect(search.searchInSet(new Set(["a", "item", "c"]), "item")).toBe(true);
230
expect(search.searchInMap(new Map([["key", "value"]]), "key")).toBe(true);
231
expect(search.searchInObject({ name: "John", age: 25 }, "name")).toBe(true);
232
expect(search.searchInObject({ name: "John", age: 25 }, "John")).toBe(true);
233
```
234
235
### Null/Undefined Matchers
236
237
Matchers for validating null, undefined, and empty values.
238
239
```typescript { .api }
240
/** Matches values that are not null */
241
const notNull: MatcherCreator<any>;
242
243
/** Matches values that are not undefined */
244
const notUndefined: MatcherCreator<any>;
245
246
/** Matches values that are not null, undefined, or empty string */
247
const notEmpty: MatcherCreator<any>;
248
```
249
250
**Usage Examples:**
251
252
```typescript
253
import { mock, notNull, notUndefined, notEmpty } from "jest-mock-extended";
254
255
interface ValidationService {
256
validateRequired: (value: any) => boolean;
257
validateOptional: (value: any) => boolean;
258
validateNonEmpty: (value: any) => boolean;
259
}
260
261
const validator = mock<ValidationService>();
262
263
// Null/undefined validation
264
validator.validateRequired.calledWith(notNull()).mockReturnValue(true);
265
validator.validateOptional.calledWith(notUndefined()).mockReturnValue(true);
266
validator.validateNonEmpty.calledWith(notEmpty()).mockReturnValue(true);
267
268
// Test validation
269
expect(validator.validateRequired("value")).toBe(true);
270
expect(validator.validateOptional(0)).toBe(true);
271
expect(validator.validateNonEmpty("text")).toBe(true);
272
273
// These would not match the expectations
274
// validator.validateRequired(null); // Would not match notNull()
275
// validator.validateOptional(undefined); // Would not match notUndefined()
276
// validator.validateNonEmpty(""); // Would not match notEmpty()
277
```
278
279
### Argument Captor
280
281
Special matcher for capturing argument values during mock function calls.
282
283
```typescript { .api }
284
/**
285
* Captor matcher for capturing argument values
286
*/
287
class CaptorMatcher<T> {
288
$$typeof: symbol;
289
readonly asymmetricMatch: MatcherFn<T>;
290
/** Last captured value */
291
readonly value: T;
292
/** All captured values in order */
293
readonly values: T[];
294
295
constructor();
296
getExpectedType(): string;
297
toString(): string;
298
toAsymmetricMatcher(): string;
299
}
300
301
/**
302
* Creates an argument captor for capturing call arguments
303
* @returns Captor matcher instance
304
*/
305
const captor: <T = any>() => CaptorMatcher<T>;
306
```
307
308
**Usage Examples:**
309
310
```typescript
311
import { mock, captor } from "jest-mock-extended";
312
313
interface EventLogger {
314
logEvent: (event: string, data: any, timestamp: number) => void;
315
logError: (error: Error, context: string) => void;
316
}
317
318
const logger = mock<EventLogger>();
319
320
// Create captors for different argument types
321
const eventCaptor = captor<string>();
322
const dataCaptor = captor<any>();
323
const timestampCaptor = captor<number>();
324
325
// Set up captor expectations
326
logger.logEvent
327
.calledWith(eventCaptor, dataCaptor, timestampCaptor)
328
.mockReturnValue(undefined);
329
330
// Make calls that will be captured
331
logger.logEvent("user:login", { userId: "123" }, Date.now());
332
logger.logEvent("user:logout", { userId: "123", reason: "timeout" }, Date.now());
333
334
// Access captured values
335
expect(eventCaptor.value).toBe("user:logout"); // Last captured value
336
expect(eventCaptor.values).toEqual(["user:login", "user:logout"]); // All values
337
338
expect(dataCaptor.values[0]).toEqual({ userId: "123" });
339
expect(dataCaptor.values[1]).toEqual({ userId: "123", reason: "timeout" });
340
341
expect(typeof timestampCaptor.value).toBe("number");
342
```
343
344
### Custom Matcher Creation
345
346
Create custom matchers with specific validation logic.
347
348
```typescript { .api }
349
/**
350
* Creates a custom matcher from a predicate function
351
* @param matcher - Function that returns true for matching values
352
* @returns Custom matcher instance
353
*/
354
const matches: <T = any>(matcher: MatcherFn<T>) => Matcher<T>;
355
356
/**
357
* Interface for creating reusable matcher functions
358
*/
359
interface MatcherCreator<T, E = T> {
360
(expectedValue?: E): Matcher<T>;
361
}
362
```
363
364
**Usage Examples:**
365
366
```typescript
367
import { mock, matches, MatcherCreator, Matcher } from "jest-mock-extended";
368
369
interface StringValidator {
370
validateEmail: (email: string) => boolean;
371
validatePhone: (phone: string) => boolean;
372
validateLength: (text: string, min: number, max: number) => boolean;
373
}
374
375
const validator = mock<StringValidator>();
376
377
// Simple custom matcher
378
const isValidEmail = matches<string>((email) =>
379
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
380
);
381
382
validator.validateEmail.calledWith(isValidEmail).mockReturnValue(true);
383
384
// Reusable custom matcher creator
385
const hasLength: MatcherCreator<string, number> = (expectedLength) =>
386
new Matcher(
387
(actualValue) => actualValue.length === expectedLength,
388
`hasLength(${expectedLength})`
389
);
390
391
const isLongerThan: MatcherCreator<string, number> = (minLength) =>
392
new Matcher(
393
(actualValue) => actualValue.length > minLength,
394
`isLongerThan(${minLength})`
395
);
396
397
// Use custom matchers
398
validator.validateLength
399
.calledWith(hasLength(10), 5, 15)
400
.mockReturnValue(true);
401
402
validator.validatePhone
403
.calledWith(isLongerThan(9))
404
.mockReturnValue(true);
405
406
// Test with custom matchers
407
expect(validator.validateEmail("user@example.com")).toBe(true);
408
expect(validator.validateLength("1234567890", 5, 15)).toBe(true);
409
expect(validator.validatePhone("1234567890")).toBe(true);
410
```