Expressive, readable, framework-agnostic BDD-style assertion library for JavaScript testing.
—
Methods for testing array and object containment, deep equality, and collection membership.
Test for deep equality, recursively comparing object and array contents.
/**
* Assert deep equality, comparing object contents recursively
* @param expected - The expected value or object
* @param description - Optional error message
* @returns This assertion for chaining
*/
eql(expected: any, description?: string): Assertion;Usage:
import should from 'should';
// Object deep equality
const user1 = { name: 'john', age: 30 };
const user2 = { name: 'john', age: 30 };
user1.should.eql(user2); // Different objects, same content
// Array deep equality
[1, 2, 3].should.eql([1, 2, 3]);
['a', 'b'].should.eql(['a', 'b']);
// Nested objects
const config1 = {
database: { host: 'localhost', port: 5432 },
cache: { enabled: true }
};
const config2 = {
database: { host: 'localhost', port: 5432 },
cache: { enabled: true }
};
config1.should.eql(config2);
// With description
const actual = { status: 'success', data: [] };
const expected = { status: 'success', data: [] };
actual.should.eql(expected, 'Response should match expected format');Alias for eql() - deep equality comparison.
/**
* Assert deep equality - alias for eql()
* @param expected - The expected value or object
* @param description - Optional error message
* @returns This assertion for chaining
*/
deepEqual(expected: any, description?: string): Assertion;Usage:
// Same functionality as eql()
({ a: 1, b: 2 }).should.deepEqual({ a: 1, b: 2 });
[{ id: 1 }, { id: 2 }].should.deepEqual([{ id: 1 }, { id: 2 }]);
// Complex nested structures
const tree1 = {
root: {
left: { value: 1, children: [] },
right: { value: 2, children: [{ value: 3 }] }
}
};
const tree2 = {
root: {
left: { value: 1, children: [] },
right: { value: 2, children: [{ value: 3 }] }
}
};
tree1.should.deepEqual(tree2);Test that an array or object contains an element that deeply equals the expected value.
/**
* Assert that array/object contains an element deeply equal to expected
* @param expected - The value to find in the collection
* @returns This assertion for chaining
*/
containEql(expected: any): Assertion;Usage:
// Array containment
[1, 2, 3].should.containEql(2);
['a', 'b', 'c'].should.containEql('b');
// Object containment in arrays
const users = [
{ name: 'john', age: 30 },
{ name: 'jane', age: 25 },
{ name: 'bob', age: 35 }
];
users.should.containEql({ name: 'jane', age: 25 });
// Nested object matching
const products = [
{
id: 1,
details: { name: 'laptop', price: 1000 },
tags: ['electronics', 'computer']
},
{
id: 2,
details: { name: 'phone', price: 500 },
tags: ['electronics', 'mobile']
}
];
products.should.containEql({
id: 1,
details: { name: 'laptop', price: 1000 },
tags: ['electronics', 'computer']
});
// String containment in arrays
const permissions = ['read', 'write', 'delete'];
permissions.should.containEql('write');Test that an object contains the expected properties and values (partial matching).
/**
* Assert that object contains all properties from expected (partial matching)
* @param expected - Object with properties that must be present
* @returns This assertion for chaining
*/
containDeep(expected: any): Assertion;Usage:
// Partial object matching
const user = {
id: 123,
name: 'john',
email: 'john@test.com',
preferences: { theme: 'dark', lang: 'en' }
};
// Contains these properties (others may exist)
user.should.containDeep({ name: 'john', id: 123 });
user.should.containDeep({
preferences: { theme: 'dark' }
}); // Nested partial matching
// Array with partial object matching
const items = [
{ id: 1, name: 'item1', category: 'A', active: true },
{ id: 2, name: 'item2', category: 'B', active: false },
{ id: 3, name: 'item3', category: 'A', active: true }
];
items.should.containDeep([
{ id: 1, name: 'item1' }, // Partial match - category and active ignored
{ id: 2, active: false } // Partial match - name and category ignored
]);
// API response validation
const apiResponse = {
status: 200,
data: {
users: [
{ id: 1, name: 'john', role: 'admin', lastLogin: '2023-01-01' },
{ id: 2, name: 'jane', role: 'user', lastLogin: '2023-01-02' }
],
pagination: { page: 1, total: 2 }
},
meta: { timestamp: 1234567890 }
};
apiResponse.should.containDeep({
status: 200,
data: {
users: [
{ id: 1, name: 'john' }, // Don't care about role, lastLogin
{ id: 2, name: 'jane' } // Don't care about role, lastLogin
]
}
});Test that an array contains elements in the specified order with deep equality.
/**
* Assert that array contains elements in specified order (deep equality)
* @param expected - Array of elements that should be present in order
* @returns This assertion for chaining
*/
containDeepOrdered(expected: any): Assertion;Usage:
// Ordered containment
const sequence = [
{ step: 1, action: 'start' },
{ step: 2, action: 'process' },
{ step: 3, action: 'validate' },
{ step: 4, action: 'finish' }
];
// Must contain these in order (other elements can exist between)
sequence.should.containDeepOrdered([
{ step: 1, action: 'start' },
{ step: 3, action: 'validate' },
{ step: 4, action: 'finish' }
]);
// Event log validation
const eventLog = [
{ type: 'user_login', user: 'john', timestamp: 1001 },
{ type: 'page_view', page: '/dashboard', timestamp: 1002 },
{ type: 'button_click', element: 'save', timestamp: 1003 },
{ type: 'api_call', endpoint: '/save', timestamp: 1004 },
{ type: 'user_logout', user: 'john', timestamp: 1005 }
];
// Verify sequence of critical events
eventLog.should.containDeepOrdered([
{ type: 'user_login', user: 'john' },
{ type: 'api_call', endpoint: '/save' },
{ type: 'user_logout', user: 'john' }
]);
// Simple array ordering
[1, 5, 2, 8, 3, 9].should.containDeepOrdered([1, 2, 3]); // In order
[1, 5, 2, 8, 3, 9].should.not.containDeepOrdered([3, 2, 1]); // Wrong order// Complex data structure
const applicationState = {
user: {
id: 123,
profile: { name: 'john', email: 'john@test.com' },
permissions: ['read', 'write']
},
ui: {
theme: 'dark',
notifications: [
{ id: 1, type: 'info', message: 'Welcome' },
{ id: 2, type: 'warn', message: 'Update required' }
]
}
};
// Test nested structure contains expected data
applicationState.should.containDeep({
user: {
id: 123,
permissions: ['read', 'write']
},
ui: {
theme: 'dark'
}
});
// Test array within nested object
applicationState.ui.notifications.should.containEql({
id: 1,
type: 'info',
message: 'Welcome'
});function validateUserListResponse(response) {
// Overall structure
response.should.be.an.Object();
response.should.have.properties('data', 'meta', 'status');
response.status.should.equal(200);
// Data array validation
response.data.should.be.an.Array();
response.data.should.not.be.empty();
// Each user has required fields
response.data.forEach(user => {
user.should.have.properties('id', 'name', 'email');
user.id.should.be.a.Number();
user.name.should.be.a.String();
user.email.should.match(/\S+@\S+\.\S+/);
});
// Contains specific user
response.data.should.containEql({
id: 123,
name: 'john',
email: 'john@test.com'
});
}
const apiResponse = {
status: 200,
data: [
{ id: 123, name: 'john', email: 'john@test.com', role: 'admin' },
{ id: 124, name: 'jane', email: 'jane@test.com', role: 'user' }
],
meta: { total: 2, page: 1 }
};
validateUserListResponse(apiResponse);function validateConfig(config) {
// Must contain required configuration sections
config.should.containDeep({
database: {
host: 'localhost'
},
server: {
port: 3000
}
});
// Database config contains connection settings
config.database.should.have.properties('host', 'port', 'database');
// Environment-specific settings
if (config.environment === 'production') {
config.should.containDeep({
ssl: { enabled: true },
logging: { level: 'warn' }
});
}
}
const devConfig = {
environment: 'development',
database: {
host: 'localhost',
port: 5432,
database: 'myapp_dev',
ssl: false
},
server: {
port: 3000,
cors: true
},
logging: {
level: 'debug',
format: 'json'
}
};
validateConfig(devConfig);// Large dataset containment
const bigArray = Array.from({ length: 10000 }, (_, i) => ({
id: i,
data: `item_${i}`,
active: i % 2 === 0
}));
// Efficient containment testing
bigArray.should.containEql({ id: 5000, data: 'item_5000', active: true });
// Partial matching in large datasets
bigArray.should.containDeep([
{ id: 0, active: true },
{ id: 1, active: false },
{ id: 2, active: true }
]);// Negation examples
[1, 2, 3].should.not.containEql(4);
[1, 2, 3].should.not.eql([3, 2, 1]); // Order matters for eql
const obj = { name: 'john', age: 30 };
obj.should.not.containDeep({ name: 'jane' });
obj.should.not.eql({ name: 'john' }); // Missing age property
// Edge cases
[].should.not.containEql(1); // Empty array
{}.should.not.containDeep({ key: 'value' }); // Empty object
// null and undefined handling
[null, undefined, 0, false].should.containEql(null);
[null, undefined, 0, false].should.containEql(undefined);const data = [
{ id: 1, name: 'john', scores: [85, 90, 88] },
{ id: 2, name: 'jane', scores: [92, 89, 94] }
];
// Chain containment with other assertions
data.should.be.an.Array()
.and.have.length(2)
.and.containEql({ id: 1, name: 'john', scores: [85, 90, 88] });
// Test nested array properties
const user = data.find(u => u.id === 1);
user.should.have.property('scores')
.which.is.an.Array()
.and.containEql(90)
.and.have.length(3);Install with Tessl CLI
npx tessl i tessl/npm-should