0
# Collection and Containment Assertions
1
2
Methods for testing array and object containment, deep equality, and collection membership.
3
4
## Deep Equality Testing
5
6
### eql()
7
8
Test for deep equality, recursively comparing object and array contents.
9
10
```javascript { .api }
11
/**
12
* Assert deep equality, comparing object contents recursively
13
* @param expected - The expected value or object
14
* @param description - Optional error message
15
* @returns This assertion for chaining
16
*/
17
eql(expected: any, description?: string): Assertion;
18
```
19
20
**Usage:**
21
```javascript
22
import should from 'should';
23
24
// Object deep equality
25
const user1 = { name: 'john', age: 30 };
26
const user2 = { name: 'john', age: 30 };
27
user1.should.eql(user2); // Different objects, same content
28
29
// Array deep equality
30
[1, 2, 3].should.eql([1, 2, 3]);
31
['a', 'b'].should.eql(['a', 'b']);
32
33
// Nested objects
34
const config1 = {
35
database: { host: 'localhost', port: 5432 },
36
cache: { enabled: true }
37
};
38
const config2 = {
39
database: { host: 'localhost', port: 5432 },
40
cache: { enabled: true }
41
};
42
config1.should.eql(config2);
43
44
// With description
45
const actual = { status: 'success', data: [] };
46
const expected = { status: 'success', data: [] };
47
actual.should.eql(expected, 'Response should match expected format');
48
```
49
50
### deepEqual()
51
52
Alias for `eql()` - deep equality comparison.
53
54
```javascript { .api }
55
/**
56
* Assert deep equality - alias for eql()
57
* @param expected - The expected value or object
58
* @param description - Optional error message
59
* @returns This assertion for chaining
60
*/
61
deepEqual(expected: any, description?: string): Assertion;
62
```
63
64
**Usage:**
65
```javascript
66
// Same functionality as eql()
67
({ a: 1, b: 2 }).should.deepEqual({ a: 1, b: 2 });
68
[{ id: 1 }, { id: 2 }].should.deepEqual([{ id: 1 }, { id: 2 }]);
69
70
// Complex nested structures
71
const tree1 = {
72
root: {
73
left: { value: 1, children: [] },
74
right: { value: 2, children: [{ value: 3 }] }
75
}
76
};
77
const tree2 = {
78
root: {
79
left: { value: 1, children: [] },
80
right: { value: 2, children: [{ value: 3 }] }
81
}
82
};
83
tree1.should.deepEqual(tree2);
84
```
85
86
## Containment Testing
87
88
### containEql()
89
90
Test that an array or object contains an element that deeply equals the expected value.
91
92
```javascript { .api }
93
/**
94
* Assert that array/object contains an element deeply equal to expected
95
* @param expected - The value to find in the collection
96
* @returns This assertion for chaining
97
*/
98
containEql(expected: any): Assertion;
99
```
100
101
**Usage:**
102
```javascript
103
// Array containment
104
[1, 2, 3].should.containEql(2);
105
['a', 'b', 'c'].should.containEql('b');
106
107
// Object containment in arrays
108
const users = [
109
{ name: 'john', age: 30 },
110
{ name: 'jane', age: 25 },
111
{ name: 'bob', age: 35 }
112
];
113
users.should.containEql({ name: 'jane', age: 25 });
114
115
// Nested object matching
116
const products = [
117
{
118
id: 1,
119
details: { name: 'laptop', price: 1000 },
120
tags: ['electronics', 'computer']
121
},
122
{
123
id: 2,
124
details: { name: 'phone', price: 500 },
125
tags: ['electronics', 'mobile']
126
}
127
];
128
products.should.containEql({
129
id: 1,
130
details: { name: 'laptop', price: 1000 },
131
tags: ['electronics', 'computer']
132
});
133
134
// String containment in arrays
135
const permissions = ['read', 'write', 'delete'];
136
permissions.should.containEql('write');
137
```
138
139
### containDeep()
140
141
Test that an object contains the expected properties and values (partial matching).
142
143
```javascript { .api }
144
/**
145
* Assert that object contains all properties from expected (partial matching)
146
* @param expected - Object with properties that must be present
147
* @returns This assertion for chaining
148
*/
149
containDeep(expected: any): Assertion;
150
```
151
152
**Usage:**
153
```javascript
154
// Partial object matching
155
const user = {
156
id: 123,
157
name: 'john',
158
email: 'john@test.com',
159
preferences: { theme: 'dark', lang: 'en' }
160
};
161
162
// Contains these properties (others may exist)
163
user.should.containDeep({ name: 'john', id: 123 });
164
user.should.containDeep({
165
preferences: { theme: 'dark' }
166
}); // Nested partial matching
167
168
// Array with partial object matching
169
const items = [
170
{ id: 1, name: 'item1', category: 'A', active: true },
171
{ id: 2, name: 'item2', category: 'B', active: false },
172
{ id: 3, name: 'item3', category: 'A', active: true }
173
];
174
175
items.should.containDeep([
176
{ id: 1, name: 'item1' }, // Partial match - category and active ignored
177
{ id: 2, active: false } // Partial match - name and category ignored
178
]);
179
180
// API response validation
181
const apiResponse = {
182
status: 200,
183
data: {
184
users: [
185
{ id: 1, name: 'john', role: 'admin', lastLogin: '2023-01-01' },
186
{ id: 2, name: 'jane', role: 'user', lastLogin: '2023-01-02' }
187
],
188
pagination: { page: 1, total: 2 }
189
},
190
meta: { timestamp: 1234567890 }
191
};
192
193
apiResponse.should.containDeep({
194
status: 200,
195
data: {
196
users: [
197
{ id: 1, name: 'john' }, // Don't care about role, lastLogin
198
{ id: 2, name: 'jane' } // Don't care about role, lastLogin
199
]
200
}
201
});
202
```
203
204
### containDeepOrdered()
205
206
Test that an array contains elements in the specified order with deep equality.
207
208
```javascript { .api }
209
/**
210
* Assert that array contains elements in specified order (deep equality)
211
* @param expected - Array of elements that should be present in order
212
* @returns This assertion for chaining
213
*/
214
containDeepOrdered(expected: any): Assertion;
215
```
216
217
**Usage:**
218
```javascript
219
// Ordered containment
220
const sequence = [
221
{ step: 1, action: 'start' },
222
{ step: 2, action: 'process' },
223
{ step: 3, action: 'validate' },
224
{ step: 4, action: 'finish' }
225
];
226
227
// Must contain these in order (other elements can exist between)
228
sequence.should.containDeepOrdered([
229
{ step: 1, action: 'start' },
230
{ step: 3, action: 'validate' },
231
{ step: 4, action: 'finish' }
232
]);
233
234
// Event log validation
235
const eventLog = [
236
{ type: 'user_login', user: 'john', timestamp: 1001 },
237
{ type: 'page_view', page: '/dashboard', timestamp: 1002 },
238
{ type: 'button_click', element: 'save', timestamp: 1003 },
239
{ type: 'api_call', endpoint: '/save', timestamp: 1004 },
240
{ type: 'user_logout', user: 'john', timestamp: 1005 }
241
];
242
243
// Verify sequence of critical events
244
eventLog.should.containDeepOrdered([
245
{ type: 'user_login', user: 'john' },
246
{ type: 'api_call', endpoint: '/save' },
247
{ type: 'user_logout', user: 'john' }
248
]);
249
250
// Simple array ordering
251
[1, 5, 2, 8, 3, 9].should.containDeepOrdered([1, 2, 3]); // In order
252
[1, 5, 2, 8, 3, 9].should.not.containDeepOrdered([3, 2, 1]); // Wrong order
253
```
254
255
## Complex Collection Testing
256
257
### Nested Array and Object Validation
258
```javascript
259
// Complex data structure
260
const applicationState = {
261
user: {
262
id: 123,
263
profile: { name: 'john', email: 'john@test.com' },
264
permissions: ['read', 'write']
265
},
266
ui: {
267
theme: 'dark',
268
notifications: [
269
{ id: 1, type: 'info', message: 'Welcome' },
270
{ id: 2, type: 'warn', message: 'Update required' }
271
]
272
}
273
};
274
275
// Test nested structure contains expected data
276
applicationState.should.containDeep({
277
user: {
278
id: 123,
279
permissions: ['read', 'write']
280
},
281
ui: {
282
theme: 'dark'
283
}
284
});
285
286
// Test array within nested object
287
applicationState.ui.notifications.should.containEql({
288
id: 1,
289
type: 'info',
290
message: 'Welcome'
291
});
292
```
293
294
### API Response Validation
295
```javascript
296
function validateUserListResponse(response) {
297
// Overall structure
298
response.should.be.an.Object();
299
response.should.have.properties('data', 'meta', 'status');
300
response.status.should.equal(200);
301
302
// Data array validation
303
response.data.should.be.an.Array();
304
response.data.should.not.be.empty();
305
306
// Each user has required fields
307
response.data.forEach(user => {
308
user.should.have.properties('id', 'name', 'email');
309
user.id.should.be.a.Number();
310
user.name.should.be.a.String();
311
user.email.should.match(/\S+@\S+\.\S+/);
312
});
313
314
// Contains specific user
315
response.data.should.containEql({
316
id: 123,
317
name: 'john',
318
email: 'john@test.com'
319
});
320
}
321
322
const apiResponse = {
323
status: 200,
324
data: [
325
{ id: 123, name: 'john', email: 'john@test.com', role: 'admin' },
326
{ id: 124, name: 'jane', email: 'jane@test.com', role: 'user' }
327
],
328
meta: { total: 2, page: 1 }
329
};
330
331
validateUserListResponse(apiResponse);
332
```
333
334
### Configuration Validation
335
```javascript
336
function validateConfig(config) {
337
// Must contain required configuration sections
338
config.should.containDeep({
339
database: {
340
host: 'localhost'
341
},
342
server: {
343
port: 3000
344
}
345
});
346
347
// Database config contains connection settings
348
config.database.should.have.properties('host', 'port', 'database');
349
350
// Environment-specific settings
351
if (config.environment === 'production') {
352
config.should.containDeep({
353
ssl: { enabled: true },
354
logging: { level: 'warn' }
355
});
356
}
357
}
358
359
const devConfig = {
360
environment: 'development',
361
database: {
362
host: 'localhost',
363
port: 5432,
364
database: 'myapp_dev',
365
ssl: false
366
},
367
server: {
368
port: 3000,
369
cors: true
370
},
371
logging: {
372
level: 'debug',
373
format: 'json'
374
}
375
};
376
377
validateConfig(devConfig);
378
```
379
380
## Performance and Memory Testing
381
```javascript
382
// Large dataset containment
383
const bigArray = Array.from({ length: 10000 }, (_, i) => ({
384
id: i,
385
data: `item_${i}`,
386
active: i % 2 === 0
387
}));
388
389
// Efficient containment testing
390
bigArray.should.containEql({ id: 5000, data: 'item_5000', active: true });
391
392
// Partial matching in large datasets
393
bigArray.should.containDeep([
394
{ id: 0, active: true },
395
{ id: 1, active: false },
396
{ id: 2, active: true }
397
]);
398
```
399
400
## Negation and Edge Cases
401
402
```javascript
403
// Negation examples
404
[1, 2, 3].should.not.containEql(4);
405
[1, 2, 3].should.not.eql([3, 2, 1]); // Order matters for eql
406
407
const obj = { name: 'john', age: 30 };
408
obj.should.not.containDeep({ name: 'jane' });
409
obj.should.not.eql({ name: 'john' }); // Missing age property
410
411
// Edge cases
412
[].should.not.containEql(1); // Empty array
413
{}.should.not.containDeep({ key: 'value' }); // Empty object
414
415
// null and undefined handling
416
[null, undefined, 0, false].should.containEql(null);
417
[null, undefined, 0, false].should.containEql(undefined);
418
```
419
420
## Chaining with Other Assertions
421
422
```javascript
423
const data = [
424
{ id: 1, name: 'john', scores: [85, 90, 88] },
425
{ id: 2, name: 'jane', scores: [92, 89, 94] }
426
];
427
428
// Chain containment with other assertions
429
data.should.be.an.Array()
430
.and.have.length(2)
431
.and.containEql({ id: 1, name: 'john', scores: [85, 90, 88] });
432
433
// Test nested array properties
434
const user = data.find(u => u.id === 1);
435
user.should.have.property('scores')
436
.which.is.an.Array()
437
.and.containEql(90)
438
.and.have.length(3);
439
```