0
# Asymmetric Matchers
1
2
Special matchers for flexible pattern matching and partial object comparison. These matchers allow for more flexible assertions when exact matching is not practical or desired.
3
4
## Capabilities
5
6
### General Matching
7
8
#### expect.anything
9
10
Matches any value except `null` and `undefined`.
11
12
```javascript { .api }
13
/**
14
* Matches anything except null or undefined
15
* @returns Anything matcher instance
16
*/
17
expect.anything(): Anything;
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
// Use in arrays
24
expect(['alice', 'bob', 'charlie']).toEqual([
25
'alice',
26
expect.anything(),
27
'charlie'
28
]);
29
30
// Use in objects
31
expect({
32
name: 'John',
33
id: 123,
34
timestamp: new Date()
35
}).toEqual({
36
name: 'John',
37
id: expect.anything(),
38
timestamp: expect.anything()
39
});
40
41
// Use with function calls
42
const mockFn = jest.fn();
43
mockFn('hello', 42, true);
44
45
expect(mockFn).toHaveBeenCalledWith(
46
'hello',
47
expect.anything(),
48
expect.anything()
49
);
50
51
// Won't match null or undefined
52
expect({value: null}).not.toEqual({value: expect.anything()});
53
expect({value: undefined}).not.toEqual({value: expect.anything()});
54
```
55
56
#### expect.any
57
58
Matches any instance of the specified constructor or type.
59
60
```javascript { .api }
61
/**
62
* Matches any instance of given constructor
63
* @param constructor - Constructor function or class to match against
64
* @returns Any matcher instance
65
*/
66
expect.any(constructor: Function): Any;
67
```
68
69
**Usage Examples:**
70
71
```javascript
72
// Primitive types
73
expect('hello').toEqual(expect.any(String));
74
expect(42).toEqual(expect.any(Number));
75
expect(true).toEqual(expect.any(Boolean));
76
77
// Object types
78
expect(new Date()).toEqual(expect.any(Date));
79
expect([1, 2, 3]).toEqual(expect.any(Array));
80
expect(/regex/).toEqual(expect.any(RegExp));
81
expect(new Error('test')).toEqual(expect.any(Error));
82
83
// Custom classes
84
class User {
85
constructor(name) {
86
this.name = name;
87
}
88
}
89
90
const user = new User('John');
91
expect(user).toEqual(expect.any(User));
92
93
// Use in complex objects
94
expect({
95
user: new User('John'),
96
createdAt: new Date(),
97
tags: ['admin', 'active']
98
}).toEqual({
99
user: expect.any(User),
100
createdAt: expect.any(Date),
101
tags: expect.any(Array)
102
});
103
104
// Use with mock function verification
105
const mockCallback = jest.fn();
106
mockCallback(new Error('something failed'));
107
108
expect(mockCallback).toHaveBeenCalledWith(expect.any(Error));
109
```
110
111
### Object Matching
112
113
#### expect.objectContaining
114
115
Matches objects that contain the specified properties (subset matching).
116
117
```javascript { .api }
118
/**
119
* Matches objects containing provided properties
120
* @param object - Object with properties that should be present
121
* @returns ObjectContaining matcher instance
122
*/
123
expect.objectContaining(object: Object): ObjectContaining;
124
```
125
126
**Usage Examples:**
127
128
```javascript
129
// Basic object containment
130
const user = {
131
id: 1,
132
name: 'John',
133
email: 'john@example.com',
134
preferences: {theme: 'dark'},
135
roles: ['user', 'admin']
136
};
137
138
expect(user).toEqual(expect.objectContaining({
139
name: 'John',
140
email: 'john@example.com'
141
}));
142
143
// Nested object containment
144
expect(user).toEqual(expect.objectContaining({
145
preferences: expect.objectContaining({
146
theme: 'dark'
147
})
148
}));
149
150
// Use in arrays
151
expect([
152
{id: 1, name: 'John'},
153
{id: 2, name: 'Jane'},
154
{id: 3, name: 'Bob'}
155
]).toContainEqual(expect.objectContaining({name: 'Jane'}));
156
157
// API response testing
158
const apiResponse = {
159
data: {id: 123, name: 'John'},
160
metadata: {timestamp: '2023-01-01', version: '1.0'},
161
status: 'success'
162
};
163
164
expect(apiResponse).toEqual(expect.objectContaining({
165
data: expect.objectContaining({id: 123}),
166
status: 'success'
167
}));
168
169
// Mock function verification
170
const mockFn = jest.fn();
171
mockFn({name: 'John', age: 30, city: 'New York'});
172
173
expect(mockFn).toHaveBeenCalledWith(
174
expect.objectContaining({name: 'John', age: 30})
175
);
176
```
177
178
### Array Matching
179
180
#### expect.arrayContaining
181
182
Matches arrays that contain all of the specified elements.
183
184
```javascript { .api }
185
/**
186
* Matches arrays containing all provided elements
187
* @param array - Array with elements that should be present
188
* @returns ArrayContaining matcher instance
189
*/
190
expect.arrayContaining(array: Array): ArrayContaining;
191
```
192
193
**Usage Examples:**
194
195
```javascript
196
// Basic array containment
197
expect(['a', 'b', 'c', 'd', 'e']).toEqual(
198
expect.arrayContaining(['a', 'c', 'e'])
199
);
200
201
// Order doesn't matter
202
expect(['z', 'y', 'x']).toEqual(
203
expect.arrayContaining(['x', 'z'])
204
);
205
206
// Works with objects
207
expect([
208
{name: 'Alice', age: 25},
209
{name: 'Bob', age: 30},
210
{name: 'Charlie', age: 35}
211
]).toEqual(expect.arrayContaining([
212
{name: 'Alice', age: 25},
213
{name: 'Charlie', age: 35}
214
]));
215
216
// Nested in objects
217
expect({
218
users: ['john', 'jane', 'bob'],
219
admins: ['alice', 'charlie']
220
}).toEqual({
221
users: expect.arrayContaining(['john', 'jane']),
222
admins: expect.arrayContaining(['alice'])
223
});
224
225
// Testing API responses
226
const searchResults = {
227
results: [
228
{id: 1, title: 'First Post'},
229
{id: 2, title: 'Second Post'},
230
{id: 3, title: 'Third Post'}
231
]
232
};
233
234
expect(searchResults).toEqual({
235
results: expect.arrayContaining([
236
expect.objectContaining({title: 'First Post'}),
237
expect.objectContaining({title: 'Third Post'})
238
])
239
});
240
```
241
242
### String Matching
243
244
#### expect.stringContaining
245
246
Matches strings that contain the specified substring.
247
248
```javascript { .api }
249
/**
250
* Matches strings containing provided substring
251
* @param string - Substring that should be present
252
* @returns StringContaining matcher instance
253
*/
254
expect.stringContaining(string: string): StringContaining;
255
```
256
257
**Usage Examples:**
258
259
```javascript
260
// Basic substring matching
261
expect('Hello World').toEqual(expect.stringContaining('World'));
262
expect('JavaScript is awesome').toEqual(expect.stringContaining('Script'));
263
264
// Case sensitive
265
expect('Hello World').toEqual(expect.stringContaining('World'));
266
expect('Hello World').not.toEqual(expect.stringContaining('world'));
267
268
// Use in objects
269
expect({
270
message: 'User registration successful',
271
details: 'Account created for user@example.com'
272
}).toEqual({
273
message: expect.stringContaining('successful'),
274
details: expect.stringContaining('user@example.com')
275
});
276
277
// Use in arrays
278
expect(['error: file not found', 'warning: deprecated method']).toEqual([
279
expect.stringContaining('error:'),
280
expect.stringContaining('warning:')
281
]);
282
283
// Testing log messages
284
const logMessages = [
285
'INFO: Application started',
286
'DEBUG: Database connected',
287
'ERROR: Failed to process request'
288
];
289
290
expect(logMessages).toEqual(expect.arrayContaining([
291
expect.stringContaining('Application started'),
292
expect.stringContaining('ERROR:')
293
]));
294
```
295
296
#### expect.stringMatching
297
298
Matches strings that match the specified regular expression pattern.
299
300
```javascript { .api }
301
/**
302
* Matches strings matching provided pattern or regex
303
* @param pattern - String pattern or RegExp to match against
304
* @returns StringMatching matcher instance
305
*/
306
expect.stringMatching(pattern: string | RegExp): StringMatching;
307
```
308
309
**Usage Examples:**
310
311
```javascript
312
// Basic regex matching
313
expect('hello@example.com').toEqual(
314
expect.stringMatching(/^[\w\.-]+@[\w\.-]+\.\w+$/)
315
);
316
317
expect('2023-12-25').toEqual(
318
expect.stringMatching(/^\d{4}-\d{2}-\d{2}$/)
319
);
320
321
// String pattern matching
322
expect('user123').toEqual(expect.stringMatching('user'));
323
324
// Case insensitive matching
325
expect('JavaScript').toEqual(expect.stringMatching(/javascript/i));
326
327
// Use in complex objects
328
expect({
329
user: {
330
email: 'john.doe@company.com',
331
phone: '(555) 123-4567',
332
id: 'user_abc123'
333
}
334
}).toEqual({
335
user: {
336
email: expect.stringMatching(/@company\.com$/),
337
phone: expect.stringMatching(/^\(\d{3}\) \d{3}-\d{4}$/),
338
id: expect.stringMatching(/^user_/)
339
}
340
});
341
342
// Testing URL patterns
343
const apiUrls = [
344
'https://api.example.com/v1/users',
345
'https://api.example.com/v1/posts',
346
'https://api.example.com/v2/comments'
347
];
348
349
expect(apiUrls).toEqual(expect.arrayContaining([
350
expect.stringMatching(/\/v1\/users$/),
351
expect.stringMatching(/\/v2\//)
352
]));
353
```
354
355
## Advanced Usage Patterns
356
357
### Combining Asymmetric Matchers
358
359
```javascript
360
// Complex nested matching
361
const complexObject = {
362
user: {
363
id: 'user_123',
364
profile: {
365
name: 'John Doe',
366
email: 'john@example.com',
367
settings: {
368
notifications: true,
369
preferences: ['email', 'sms']
370
}
371
}
372
},
373
metadata: {
374
createdAt: new Date(),
375
version: '1.2.3'
376
}
377
};
378
379
expect(complexObject).toEqual({
380
user: expect.objectContaining({
381
id: expect.stringMatching(/^user_/),
382
profile: expect.objectContaining({
383
name: expect.any(String),
384
email: expect.stringContaining('@'),
385
settings: expect.objectContaining({
386
preferences: expect.arrayContaining(['email'])
387
})
388
})
389
}),
390
metadata: expect.objectContaining({
391
createdAt: expect.any(Date),
392
version: expect.stringMatching(/^\d+\.\d+\.\d+$/)
393
})
394
});
395
```
396
397
### Testing API Responses
398
399
```javascript
400
// Testing flexible API response structure
401
const apiResponse = {
402
data: [
403
{id: 1, name: 'John', email: 'john@test.com', createdAt: '2023-01-01T00:00:00Z'},
404
{id: 2, name: 'Jane', email: 'jane@test.com', createdAt: '2023-01-02T00:00:00Z'}
405
],
406
pagination: {
407
total: 2,
408
page: 1,
409
perPage: 10
410
},
411
meta: {
412
requestId: 'req_abc123',
413
timestamp: 1672531200000
414
}
415
};
416
417
expect(apiResponse).toEqual({
418
data: expect.arrayContaining([
419
expect.objectContaining({
420
id: expect.any(Number),
421
name: expect.any(String),
422
email: expect.stringMatching(/@test\.com$/),
423
createdAt: expect.stringMatching(/^\d{4}-\d{2}-\d{2}T/)
424
})
425
]),
426
pagination: expect.objectContaining({
427
total: expect.any(Number),
428
page: 1
429
}),
430
meta: expect.objectContaining({
431
requestId: expect.stringMatching(/^req_/),
432
timestamp: expect.any(Number)
433
})
434
});
435
```
436
437
### Testing Event Data
438
439
```javascript
440
// Testing event payloads with flexible structure
441
function trackEvent(eventName, properties) {
442
// Event tracking implementation
443
}
444
445
const trackEventSpy = jest.fn();
446
trackEventSpy('user_signup', {
447
userId: 'user_123',
448
email: 'user@example.com',
449
timestamp: Date.now(),
450
source: 'web',
451
properties: {
452
plan: 'premium',
453
referrer: 'google'
454
}
455
});
456
457
expect(trackEventSpy).toHaveBeenCalledWith(
458
'user_signup',
459
expect.objectContaining({
460
userId: expect.stringMatching(/^user_/),
461
email: expect.stringContaining('@'),
462
timestamp: expect.any(Number),
463
source: expect.any(String),
464
properties: expect.objectContaining({
465
plan: expect.any(String)
466
})
467
})
468
);
469
```
470
471
## Using with Regular Matchers
472
473
Asymmetric matchers can be combined with regular matchers:
474
475
```javascript
476
const mockFn = jest.fn();
477
mockFn('hello', {id: 123, name: 'John'}, ['a', 'b', 'c']);
478
479
expect(mockFn).toHaveBeenCalledWith(
480
'hello', // exact match
481
expect.objectContaining({id: 123}), // asymmetric match
482
expect.arrayContaining(['a', 'c']) // asymmetric match
483
);
484
```
485
486
## Negation Support
487
488
Asymmetric matchers support negation:
489
490
```javascript
491
expect('hello').not.toEqual(expect.stringContaining('world'));
492
expect([1, 2, 3]).not.toEqual(expect.arrayContaining([4, 5]));
493
expect({a: 1}).not.toEqual(expect.objectContaining({b: 2}));
494
```