Object Matchers
Object matchers allow you to test properties, structures, and relationships of objects and arrays in your tests.
toHaveProperty
Checks if an object has a specified property path, and optionally that the value at that path equals the expected value.
xExpect(object).toHaveProperty(path, value?)
INFO
🚀 Supports asymmetric matchers
Parameters
path
: String with dot notation or array of strings representing the property pathvalue
: (Optional) Expected value at the given path
Examples
// Basic property checks
xExpect({ name: 'John' }).toHaveProperty('name')
xExpect({ user: { id: 123 } }).toHaveProperty('user.id')
// With arrays
xExpect({ users: ['John', 'Jane'] }).toHaveProperty('users.0', 'John')
xExpect({ data: [{ id: 1 }] }).toHaveProperty(['data', 0, 'id'], 1)
// With expected values
xExpect({ age: 25 }).toHaveProperty('age', 25)
xExpect({ settings: { theme: 'dark' } }).toHaveProperty('settings.theme', 'dark')
// Negated cases
xExpect({ name: 'John' }).not.toHaveProperty('age')
xExpect({ user: { role: 'admin' } }).not.toHaveProperty('user.permissions')
Using Array Notation
You can use either dot notation or array notation for nested properties:
// These are equivalent
xExpect(obj).toHaveProperty('user.profile.name')
xExpect(obj).toHaveProperty(['user', 'profile', 'name'])
// Array notation is useful when property names contain dots
xExpect({ 'user.name': 'John' }).toHaveProperty(['user.name'])
toBeInstanceOf
Checks if a value is an instance of a specified class or constructor function.
xExpect(value).toBeInstanceOf(constructor)
Parameters
constructor
: The expected constructor function or class
Examples
// Built-in JavaScript classes
xExpect(new Date()).toBeInstanceOf(Date)
xExpect([1, 2, 3]).toBeInstanceOf(Array)
xExpect(new Map()).toBeInstanceOf(Map)
xExpect(new Set([1, 2])).toBeInstanceOf(Set)
xExpect(new Error()).toBeInstanceOf(Error)
xExpect(/abc/).toBeInstanceOf(RegExp)
// Custom classes
class User {
constructor(name) {
this.name = name
}
}
xExpect(new User('John')).toBeInstanceOf(User)
// Inheritance
class Admin extends User {}
xExpect(new Admin('Jane')).toBeInstanceOf(User)
xExpect(new Admin('Jane')).toBeInstanceOf(Admin)
// Negative cases
xExpect({}).not.toBeInstanceOf(Array)
xExpect('string').not.toBeInstanceOf(Number)
xExpect(null).not.toBeInstanceOf(Object)
toContain
Checks if an array contains a specific element or if a string contains a specific substring.
xExpect(arrayOrString).toContain(value)
Parameters
value
: The expected item or substring to find
Examples
With Arrays
// Simple values in array
xExpect([1, 2, 3]).toContain(2)
xExpect(['apple', 'banana', 'orange']).toContain('banana')
// Reference equality for objects
const obj = { id: 1 }
xExpect([obj, { id: 2 }]).toContain(obj) // Passes - same reference
xExpect([{ id: 1 }, { id: 2 }]).not.toContain({ id: 1 }) // Fails - different reference
// With strings in arrays
xExpect(['hello', 'world']).toContain('hello')
With Strings
// Substring matching
xExpect('hello world').toContain('world')
xExpect('testing is important').toContain('testing')
xExpect('apple banana orange').toContain('banana')
// Case sensitivity
xExpect('Hello World').not.toContain('hello') // Case-sensitive by default
Common Mistakes
When checking for objects or arrays in an array, toContain
uses reference equality, not structural equality:
// This will fail despite structural equality
xExpect([{ a: 1 }, { b: 2 }]).toContain({ a: 1 })
// Use toContainEqual instead for structural equality
xExpect([{ a: 1 }, { b: 2 }]).toContainEqual({ a: 1 })
toContainEqual
Checks if an array contains an element that is deeply equal to the expected value.
xExpect(array).toContainEqual(value)
INFO
🚀 Supports asymmetric matchers
Parameters
value
: The expected value to find in the array (using deep equality)
Examples
// Basic object equality
xExpect([{ a: 1 }, { b: 2 }]).toContainEqual({ a: 1 })
xExpect([{ name: 'John', age: 30 }]).toContainEqual({ name: 'John', age: 30 })
// Nested objects
xExpect([
{ user: { name: 'John', role: 'admin' } }
]).toContainEqual({
user: { name: 'John', role: 'admin' }
})
// With arrays
xExpect([[1, 2], [3, 4]]).toContainEqual([1, 2])
// With asymmetric matchers
xExpect([
{ id: 1, name: 'John' },
{ id: 2, name: 'Jane' }
]).toContainEqual(
xExpect.objectContaining({ name: 'John' })
)
// Multiple properties
xExpect([
{ id: 1, status: 'active', role: 'admin' },
{ id: 2, status: 'inactive', role: 'user' }
]).toContainEqual(
xExpect.objectContaining({
status: 'active',
role: xExpect.stringContaining('admin')
})
)
toMatchObject
Checks if an object matches the expected object by performing a deep partial equality comparison.
xExpect(object).toMatchObject(expected)
INFO
🚀 Supports asymmetric matchers
Parameters
expected
: The partial object to match against
Examples
// Basic object matching
xExpect({ name: 'John', age: 30 }).toMatchObject({ name: 'John' })
xExpect({ a: 1, b: 2, c: 3 }).toMatchObject({ a: 1, c: 3 })
// Nested objects
xExpect({
user: {
name: 'John',
profile: { role: 'admin', active: true }
}
}).toMatchObject({
user: {
name: 'John',
profile: { role: 'admin' }
}
})
// With arrays
xExpect({
users: ['John', 'Jane', 'Bob']
}).toMatchObject({
users: ['John', 'Jane']
})
// With asymmetric matchers
xExpect({
id: 123,
user: {
name: 'John',
age: 30,
created: new Date('2023-01-01')
}
}).toMatchObject({
user: {
name: xExpect.stringContaining('Jo'),
age: xExpect.any(Number),
created: xExpect.any(Date)
}
})
Important Notes
toMatchObject
checks that the received object contains all the properties in the expected object with equal values- Properties not specified in the expected object are ignored
- The comparison is deep, including nested objects and arrays
- This matcher works well for testing a subset of an object's properties
// This passes because all expected properties match
xExpect({
name: 'John',
age: 30,
address: '123 Main St' // Extra property ignored
}).toMatchObject({
name: 'John',
age: 30
})
// This fails because expected has more properties than received
xExpect({
name: 'John'
}).not.toMatchObject({
name: 'John',
age: 30
})
Common Testing Patterns
Testing API Responses
test('API returns correct user data', async () => {
const response = await fetchUser(123)
xExpect(response).toMatchObject({
id: 123,
name: xExpect.any(String),
email: xExpect.stringMatching(/^.+@.+\..+$/),
created: xExpect.any(Date)
})
})
Validating Object Structur
test('config object has required properties', () => {
const config = loadConfig()
xExpect(config).toHaveProperty('api.url')
xExpect(config).toHaveProperty('api.timeout', 5000)
xExpect(config).toHaveProperty('debug', false)
})
Testing Class Instances
test('factory creates correct instance types', () => {
const factory = new ShapeFactory()
const circle = factory.create('circle')
xExpect(circle).toBeInstanceOf(Circle)
const square = factory.create('square')
xExpect(square).toBeInstanceOf(Square)
xExpect(square).toBeInstanceOf(Shape) // inheritance
})
Filtering Collections
test('filter returns matching items', () => {
const items = [
{ id: 1, status: 'active', type: 'user' },
{ id: 2, status: 'inactive', type: 'admin' },
{ id: 3, status: 'active', type: 'admin' }
]
const result = filterItems(items, { status: 'active', type: 'admin' })
// Check if the result contains the expected item
xExpect(result).toContainEqual(
xExpect.objectContaining({ id: 3, status: 'active', type: 'admin' })
)
// Check that we don't have other items
xExpect(result).not.toContainEqual(
xExpect.objectContaining({ id: 1 })
)
})
Combining Matchers
test('user object has correct structure and values', () => {
const user = {
id: 123,
name: 'John Doe',
roles: ['admin', 'editor'],
profile: {
email: 'john@example.com',
settings: { theme: 'dark', notifications: true }
}
}
// Check basic structure
xExpect(user).toHaveProperty('id', 123)
xExpect(user).toHaveProperty('name')
xExpect(user).toHaveProperty('profile.email')
// Check array content
xExpect(user.roles).toContain('admin')
// Check nested object partially
xExpect(user).toMatchObject({
profile: {
settings: { theme: 'dark' }
}
})
})