What's the difference between '.toMatchObject' and 'objectContaining'
From looking at the docs, and my own experimentation to confirm it, the difference is in the handling of objects nested within the props passed as an expectation.
If the expectation object has a property, containing an object, which contains some but not all of the properties in the equivalent property of the actual object, then:
toMatchObject will still pass, as seen in the docs.
expect.objectContaining will fail (unless you declare that property in the expectation object itself with expect.objectContaining())
Examples (tested in Jest):
// objectContaining, with nested object, containing full props/values
// PASSES
expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
position: {
x: expect.any(Number),
y: expect.any(Number)
}
}));
// objectContaining, with nested object, containing partial props/values
// FAILS
expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
position: {
x: expect.any(Number)
}
}));
// objectContaining, with nested object, also declared with objectContaining, containing partial props/values
// PASSES
expect({ position: { x: 0, y: 0 } }).toEqual(expect.objectContaining({
position: expect.objectContaining({
x: expect.any(Number)
})
}));
// toMatchObject, with nested object, containing full props/values
// PASSES
expect({ position: { x: 0, y: 0 } }).toMatchObject({
position: {
x: expect.any(Number),
y: expect.any(Number)
}
});
// toMatchObject, with nested object, containing partial props/values
// PASSES
expect({ position: { x: 0, y: 0 } }).toMatchObject({
position: {
x: expect.any(Number)
}
});
My thinking is that expect.objectContaining
(and other matchers like it) can be used instead of literal values inside the "object" you pass to other matchers.
This example is from the docs:
test('onPress gets called with the right thing', () => {
const onPress = jest.fn();
simulatePresses(onPress);
expect(onPress).toBeCalledWith(expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}));
});
So, while they seem to do the same thing in your example, the expect.*
ones are also useful in this other way.
Even without functional differences between the two constructs, here's an example as to why expect.objectContaining
- though long and cumbersome compared to toMatchObject
, can be useful:
describe('list of X', () => {
it('should contain an element with a specific ID', () => {
const listOfItems = uut.getItems();
expect(listOfItems).toContainEqual(expect.objectContaining({id: 'some-id'}));
});
});
Even if listOfItems
contains items as such (i.e. with fields other than just the 'id') --
[
{id: 'some-id', other: 'fields'},
{id: 'some-other-id', even: 'more-fields'}
]
still expect.objectContaining
allows for a simple way of implementing the comparison as you'd expect (i.e. based strictly on the id); toMatchObject
cannot be used here at all. So while toMatchObject
is short and readable, the longer construct of the two is more generic and allows for greater flexibility as it can be utilized in ways that toMatchObject()
can't.