186

I get that .toEqual() checks equality of all fields for plain objects:

expect(
    {"key1":"pink  wool","key2":"diorite"}
).toEqual(
    {"key2":"diorite","key1":"pink wool"}
);

So this passes.

But the same is not true for arrays:

expect(["pink wool", "diorite"]).toEqual(["diorite", "pink wool"]);

There does not seem to be a matcher function that does this in the jest docs, i.e. that tests for the equality of two arrays irrespective of their elements positions. Do I have to test each element in one array against all the elements in the other and vice versa? Or is there another way?

Benjamin H Boruff
  • 2,620
  • 3
  • 16
  • 20
  • Still an isue, maybe we can make a PR for Jest? – hypeofpipe Nov 30 '20 at 20:55
  • Just replying here in case it gets someone else to re-read their own faulty code, but my issue (which seemed like the above), was actually causing a test to unexpectantly pass because I was doing `expect( ACTUAL ).toEqual( ACTUAL )` instead of `expect( EXPECTED ).toEqual( ACTUAL )`. >_ – J.M. Janzen Feb 20 '23 at 20:13
  • 1
    I'm confused why this question has the Jasmine tag. – Daniel Kaplan Aug 01 '23 at 21:12
  • 1
    @DanielKaplan, good question. I have no idea. I don't recall after 6 years (and I don't do frontend anymore :), so I removed it! Thanks! – Benjamin H Boruff Aug 02 '23 at 22:08
  • 1
    @BenjaminHBoruff Thanks I appreciate that. I can certainly relate, too :) – Daniel Kaplan Aug 04 '23 at 23:33

11 Answers11

204

There is no built-in method to compare arrays without comparing the order, but you can simply sort the arrays using .sort() before making a comparison:

expect(["ping wool", "diorite"].sort()).toEqual(["diorite", "pink wool"].sort());

You can check the example in this fiddle.

Marco
  • 1,073
  • 9
  • 22
biphobe
  • 4,525
  • 3
  • 35
  • 46
  • 46
    this will not work if you are working with array of objects. Because the object would be converted to [Object object] and won't be sorted. And your comparison may still fail. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort. – Avinav Apr 09 '19 at 21:24
  • 10
    yeah, I mean you have to provide a suitable comparator to the sort function according to the data type. – Avin Kavish Feb 10 '21 at 04:23
  • Wow, so looks like Jest is smart enough to do an item-by-item comparison? – ankush981 May 13 '22 at 18:19
  • I suggest using `toStrictEqual` over `toEqual` where possible. It is a deeper equality check. https://jestjs.io/docs/expect#tostrictequalvalue Note: using `toBe` is even deeper (exact objects), but won't work here. – aarowman Oct 27 '22 at 15:24
49

As already mentioned expect.arrayContaining checks if the actual array contains the expected array as a subset. To check for equivalence one may

  • either assert that the length of both arrays is the same (but that wouldn't result in a helpful failure message)
  • or assert the reverse: That the expected array contains the actual array:
// This is TypeScript, but remove the types and you get JavaScript
const expectArrayEquivalence = <T>(actual: T[], expected: T[]) => {
  expect(actual).toEqual(expect.arrayContaining(expected));
  expect(expected).toEqual(expect.arrayContaining(actual));
};

This still has the problem that when the test fails in the first assertion one is only made aware of the elements missing from actual and not of the extra ones that are not in expected.

onel0p3z
  • 566
  • 5
  • 11
nitzel
  • 1,565
  • 14
  • 14
35

Put the elements into a set. Jest knows how to match these.

expect(new Set(["pink wool", "diorite"])).toEqual(new Set(["diorite", "pink wool"]));
0xcaff
  • 13,085
  • 5
  • 47
  • 55
  • 3
    This was introduced in Jest 23.0.0, so won't work with previous versions. – jkondratowicz Dec 11 '18 at 13:55
  • 56
    This will ignore duplicate values, which is probably not what you want. `["a", "a", "b"]` will match `["a", "b"]`. – Jezzamon Feb 12 '19 at 06:28
  • 4
    Do not use this as duplicate elements are not counted. Avoid before you break your head – Jimmy Kane Mar 13 '20 at 13:27
  • 2
    I would highly encourage you to delete this answer as it is in almost every way inferior to the accepted. The only time I could imagine your answer to be better is a case where we have a lot of duplicates and therefore the comparison afterwards could be faster. If that is what you want to say please make it more clear. If not your answer is very misleading and can cause a lot of damage in an application as it looks fine and will work until it doesn't anymore then the damage can be dramatic. – Xanlantos Feb 02 '21 at 20:31
  • While this answer has caveats, it was exactly what I was looking for since I was trying to figure out if jest knows how to match two sets. – Dhruv Prakash Feb 23 '23 at 16:13
19

this does not answer the question exactly, but still may help people that end up here by google search:

if you only care that a subset of the array has certain elements, use expect.arrayContaining() https://jestjs.io/docs/en/expect#expectarraycontainingarray

e.g.,

expect(["ping wool", "diorite"])
  .toEqual(expect.arrayContaining(["diorite", "pink wool"]));
Ulad Kasach
  • 11,558
  • 11
  • 61
  • 87
  • 29
    This will not fail if arrays have additional values. Point was to check of equality. – Andreas Herd Oct 05 '18 at 06:47
  • 6
    You can always test the length of the array to ensure that this test matches what is expected rather than a subset of what is expected. – Zakalwe Jul 10 '20 at 10:18
  • 1
    @Zakalwe That's a good idea if the elements are guaranted to be unique. In cases like `['alice', 'alice', 'bob', 'carol']` and `['alice', 'bob', 'carol', 'carol']` checking equility of the array's length wouldn't be enough. – Alexander Ivanchenko Apr 06 '23 at 20:00
14

Another way is to use the custom matcher .toIncludeSameMembers() from jest-community/jest-extended.

Example given from the README

test('passes when arrays match in a different order', () => {
    expect([1, 2, 3]).toIncludeSameMembers([3, 1, 2]);
    expect([{ foo: 'bar' }, { baz: 'qux' }]).toIncludeSameMembers([{ baz: 'qux' }, { foo: 'bar' }]);
});

It might not make sense to import a library just for one matcher but they have a lot of other useful matchers I've find useful.

bluenote10
  • 23,414
  • 14
  • 122
  • 178
Jay Wick
  • 12,325
  • 10
  • 54
  • 78
10

What about checking the content and the length?

  expect(resultArray).toEqual(expect.arrayContaining(expectedArray));
  expect(resultArray.length).toEqual(expectedArray.length);
Peter Vogel
  • 101
  • 1
  • 4
  • this will do the correct assertion however the error message will be messed up. something like: `expected 2 to equal 1` and nothing else. – Melih Yıldız' Jul 16 '22 at 19:24
  • [toHaveLength](https://jestjs.io/docs/expect#tohavelengthnumber) would improve the error message – Ben Jones Jul 01 '23 at 01:09
5

You can combine using sets as stated in this answer with checking length of actual result and expectation. This will ignore element position and protect you from duplicated elements in the same time.

const materials = ['pink wool', 'diorite'];
const expectedMaterials = ['diorite', 'pink wool'];

expect(new Set(materials)).toEqual(new Set(expectedMaterials));
expect(materials.length).toBe(expectedMaterials.length);

EDIT: As there is suggested in comment below, this will only work for arrays with unique values.

tymzap
  • 896
  • 7
  • 10
  • 1
    be aware that this solution will work only for arrays with unique values. Try to compare ['pink wool', 'diorite', 'diorite'] with ['pink wool', 'pink wool', 'diorite'] – 2oppin Nov 29 '21 at 16:29
5

If you want to compare two arrays in JEST use the bellow model.

Official link: https://jestjs.io/docs/en/expect#expectarraycontainingarray

const array1 = ['a', 'b', 'c'];
const array2 = ['a', 'b', 'c'];
const array3 = ['a', 'b'];


it("test two arrays, this will be true", () => { 
    expect(array1).toEqual(expect.arrayContaining(array2));
});

it("test two arrays, this will be false", () => { 
    expect(array3).toEqual(expect.arrayContaining(array1));
});
Sergiu Mare
  • 1,552
  • 15
  • 17
  • 2
    Be careful as this - as the name suggests - checks if the array **contains** the items in the other array. The test will pass if you check if `expect(['a','b']).toEqual(expect.arrayContaining(['a']));` as mentioned in other answers. – Gergő Horváth Aug 07 '21 at 11:48
3

You can use jest toContainEqual to check if an array contains an element. Then just do that for each element in your expected array:

const actual = [{ foobar: 'C' }, { foo: 'A' }, { bar: 'B' }];
const expected = [{ foo: 'A' }, { bar: 'B' }, { foobar: 'C' }];

expect(actual).toContainEqual(expected[0]);
expect(actual).toContainEqual(expected[1]);
expect(actual).toContainEqual(expected[2]);

(Or put the expect statement in a loop if you have too many elements to check)

klugjo
  • 19,422
  • 8
  • 57
  • 75
1

If you don't have array of objects, then you can simply use sort() function for sorting before comparison.(mentioned in accepted answer):

expect(["ping wool", "diorite"].sort()).toEqual(["diorite", "pink wool"].sort());

However, problem arises if you have array of objects in which case sort function won't work. In this case, you need to provide custom sorting function. Example:

const x = [
{key: 'forecast', visible: true},
{key: 'pForecast', visible: false},
{key: 'effForecast', visible: true},
{key: 'effRegForecast', visible: true}
]

// In my use case, i wanted to sort by key
const sortByKey = (a, b) => { 
  if(a.key < b.key) return -1; 
  else if(a.key > b.key) return 1; 
  else return 0; 
  }

x.sort(sortByKey)
console.log(x)

Hope it helps someone someday.

mukesh210
  • 2,792
  • 2
  • 19
  • 41
1

Still a work in progress, but this should work albeit, the error messages may not be clear:

expect.extend({
  arrayContainingExactly(receivedOriginal, expected) {
    const received = [...receivedOriginal];

    if (received.length !== expected.length) return {
      message: () => `Expected array of length ${expected.length} but got an array of length ${received.length}`,
      pass: false,
    };

    const pass = expected.every((expectedItem, index) => {
      const receivedIndex = findIndex(received, receivedItem => {
          if (expectedItem.asymmetricMatch) return expectedItem.asymmetricMatch(receivedItem);
          return isEqual(expectedItem, receivedItem);
      });
      if (receivedIndex === -1) return false;
      received.splice(receivedIndex, 1);
      return true;
    });

    return {
      message: () => 'Success',
      pass,
    }
  }
});

Then use it like this:

expect(['foo', 'bar']).arrayContainingExactly(['foo']) // This should fail

or

expect({foo: ['foo', 'bar']}).toEqual({
  foo: expect.arrayContainingExactly(['bar', 'foo'])
}) // This should pass

We are looping through each value and removing it from the received array so that we can take advantage of the asymmetric matching provided by Jest. If we just wanted to do direct equivalency this could be simplified to just compare the 2 sorted arrays.

Note: This solution uses findIndex and isEqual from lodash.

gabeodess
  • 2,006
  • 21
  • 13