What's the difference between '.toMatchObject' and 'objectContaining'

JavascriptJestjs

Javascript Problem Overview


I have written the following test:

it('Can decrement the current step', function () {
    expect(reducer(TestState, { type: 'GOTO_PREVIOUS_STEP' })).toMatchObject({ currentStep: 4 });
});

it('Can decrement the current step v2', function () {
    expect(reducer(TestState, { type: 'GOTO_PREVIOUS_STEP' })).toEqual(expect.objectContaining({ currentStep: 4 }));
});

both of them seem to pass the test, is there any difference between them? is there a performance impact between them?

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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.

Solution 3 - Javascript

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJulito SanchisView Question on Stackoverflow
Solution 1 - JavascriptJonathanView Answer on Stackoverflow
Solution 2 - JavascriptDavidView Answer on Stackoverflow
Solution 3 - Javascriptd4vidiView Answer on Stackoverflow