CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-should

Expressive, readable, framework-agnostic BDD-style assertion library for JavaScript testing.

Pending
Overview
Eval results
Files

containment-assertions.mddocs/

Collection and Containment Assertions

Methods for testing array and object containment, deep equality, and collection membership.

Deep Equality Testing

eql()

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');

deepEqual()

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);

Containment Testing

containEql()

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');

containDeep()

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  
    ]
  }
});

containDeepOrdered()

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 Collection Testing

Nested Array and Object Validation

// 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'
});

API Response Validation

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);

Configuration Validation

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);

Performance and Memory Testing

// 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 and Edge Cases

// 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);

Chaining with Other Assertions

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

docs

basic-assertions.md

configuration.md

containment-assertions.md

error-assertions.md

index.md

number-assertions.md

pattern-matching.md

promise-assertions.md

property-assertions.md

string-assertions.md

type-assertions.md

tile.json