(Updated: )
/ #jest #node #javascript 

Jest Array/Object partial match with objectContaining and arrayContaining

Curious about Advanced Jest Testing Features?

Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.

Get "The Jest Handbook" (100 pages)

I want this

It’s possible to do partial matches on Arrays and Objects in Jest using expect.objectContaining and expect.arrayContaining.

expect has some powerful matcher methods to do things like the above partial matches.

Using Jest at an advanced level means using tools like these to write tests that are better isolated and less brittle (this is what I’m tryin to achieve with the Jest Handbook).

This post starts with an explanation to give context to partial matches followed by sample use-cases in a recipe/cookbook format.

We finish off by mentioning further resources that cover this topic.

Table of Contents

Why use Object/Array partial matching in Jest?

For example if we wanted to test that the following object had the right id but didn’t care about the other fields. We could write

test('id should match', () => {
  const obj = {
    id: '111',
    productName: 'Jest Handbook',
    url: 'https://jesthandbook.com'
  };
  expect(obj.id).toEqual('111');
});

Now what if we wanted to match 2 fields whose generation was tightly coupled? With naive matching, we would do:

test('id and productName should match', () => {
  const obj = {
    id: '111',
    productName: 'Jest Handbook',
    url: 'https://jesthandbook.com'
  };
  expect(obj.id).toEqual('111');
  expect(obj.productName).toEqual('Jest Handbook');
});

With Jest’s Object partial matching we can do:

test('id should match', () => {
  const obj = {
    id: '111',
    productName: 'Jest Handbook',
    url: 'https://jesthandbook.com'
  };
  expect(obj).toEqual(
    expect.objectContaining({
      id: '111'
    })
  );
});

and for multiple fields:

test('id and productName should match', () => {
  const obj = {
    id: '111',
    productName: 'Jest Handbook',
    url: 'https://jesthandbook.com'
  };
  expect(obj).toEqual(
    expect.objectContaining({
      id: '111',
      productName: 'Jest Handbook'
    })
  );
});

The latter example seems better for readability and maintainability of the test. It has 1 assertion tests but makes sure the code is rock solid.

Jest Simple Array partial match with expect.arrayContaining

To match part of an Array in Jest, we can use expect.arrayContaining(partialArray).

For example, if we want to make sure our oddArray has the right odds numbers under 10, we can do:

const oddArray = [1, 3, 5, 7, 9, 11, 13];
test('should start correctly', () => {
  expect(oddArray).toEqual(expect.arrayContaining([1, 3, 5, 7, 9]));
});

The equivalent without expect.arrayContaining would be:

const oddArray = [1, 3, 5, 7, 9, 11, 13];
test('should start correctly', () => {
  expect(oddArray).toContain(1);
  expect(oddArray).toContain(3);
  expect(oddArray).toContain(5);
  expect(oddArray).toContain(7);
  expect(oddArray).toContain(9);
});

Jest Simple Object partial match with expect.objectContaining

To match part of an Array in Jest, we can use expect.objectContaining(partialObject).

For example, if we want to make sure our user has the right id and name, we can do:

const user = {
  id: 1,
  friends: [],
  name: 'Hugo',
  url: 'https://codewithhugo.com'
};
test('should have right id and name', () => {
  expect(user).toEqual(
    expect.objectContaining({
      id: 1,
      name: 'Hugo'
    })
  );
});

The equivalent without expect.objectContaining would be:

const user = {
  id: 1,
  friends: [],
  name: 'Hugo',
  url: 'https://codewithhugo.com'
};
test('should have right id and name', () => {
  expect(user.id).toEqual(1);
  expect(user.name).toEqual('Hugo');
});

Jest Array of objects partial match with arrayContaining and objectContaining

In keeping with the user example, what if we wanted to check that we have the right ids for a list (array) of users.

By combining expect.objectContaining and expect.arrayContaining we can do a partial match on the objects in the array:

const users = [{id: 1, name: 'Hugo'}, {id: 2, name: 'Francesco'}];

test('we should have ids 1 and 2', () => {
  expect(users).toEqual(
    expect.arrayContaining([
      expect.objectContaining({id: 1}),
      expect.objectContaining({id: 2})
    ])
  );
});

Note: the parameter passed to arrayContaining must be an array, even if that array contains expect.objectContaining partial matches

To do the same without expect.objectContaining or expect.arrayContaining, we would have needed to unpack the array or use find/some:

const users = [{id: 1, name: 'Hugo'}, {id: 2, name: 'Francesco'}];
test('example 1 > we should have ids 1 and 2', () => {
  const [first, second] = users;
  expect(first.id).toEqual(1);
  expect(second.id).toEqual(2);
});
test('example 2 > we should have ids 1 and 2', () => {
  expect(users.some(({id}) => id === 1)).toBe(true);
  expect(users.some(({id}) => id === 2)).toBe(true);
});

Jest Object with nested arrays partial match with objectContaining and arrayContaining

In keeping with the user example, what if we wanted to check that we have the right ids for a list (array) of friends for a user?

By combining expect.objectContaining and expect.arrayContaining we can do a partial match on fields that are arrays in the object:

const user = {
  id: 1,
  name: 'Hugo',
  friends: [3, 5, 22]
};
test('user 3 should be a friend of user', () => {
  expect(user).toEqual(
    expect.objectContaining({
      friends: expect.arrayContaining([3])
    })
  );
});

To do the same without expect.objectContaining or expect.arrayContaining, we would have needed to use some:

const user = {
  id: 1,
  name: 'Hugo',
  friends: [3, 5, 22]
};
test('user 3 should be a friend of user', () => {
  expect(user.friends.some(f => f === 3)).toBe(true);
});

Jest array/object negative matches with not.objectContaining and not.arrayContaining

To check that something is not in an array or object, we can use expect.not.arrayContaining and expect.not.objectContaining respectively

const user = {
  id: 1,
  name: 'Hugo',
  friends: [3, 5, 22]
};
test('user 3 should be a friend of user', () => {
  expect(user).toEqual(
    expect.not.objectContaining({
      url: 'https://codewithhugo.com'
    })
  );
  // Can't be your own friend?
  expect(user.friends).toEqual(expect.not.arrayContaining([1]));
});

Further Reading

The examples for this post are at github.com/HugoDF/jest-object-array-partial-match.

Other resources for this are:

unsplash-logoMark Tegethoff

Author

Hugo Di Francesco

Co-author of "Professional JavaScript", "Front-End Development Projects with Vue.js" with Packt, "The Jest Handbook" (self-published). Hugo runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon, Elsevier and (currently) Eurostar.

Curious about Advanced Jest Testing Features?

Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library. Get "The Jest Handbook" (100 pages)