ChatGPT解决这个技术问题 Extra ChatGPT

Is there an Array equality match function that ignores element position in jest.js?

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?

Still an isue, maybe we can make a PR for Jest?

M
Marco

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.


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. developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….
yeah, I mean you have to provide a suitable comparator to the sort function according to the data type.
Wow, so looks like Jest is smart enough to do an item-by-item comparison?
0
0xcaff

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

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

This was introduced in Jest 23.0.0, so won't work with previous versions.
This will ignore duplicate values, which is probably not what you want. ["a", "a", "b"] will match ["a", "b"].
Do not use this as duplicate elements are not counted. Avoid before you break your head
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.
n
nitzel

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.


U
Ulad Kasach

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"]));

This will not fail if arrays have additional values. Point was to check of equality.
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.
b
bluenote10

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.


t
tymzap

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.


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']
S
Sergiu Mare

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

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.
P
Peter Vogel

What about checking the content and the length?

  expect(resultArray).toEqual(expect.arrayContaining(expectedArray));
  expect(resultArray.length).toEqual(expectedArray.length);

this will do the correct assertion however the error message will be messed up. something like: expected 2 to equal 1 and nothing else.
m
mukesh210

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.


more compact way: const sortByKey = (a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0)
g
gabeodess

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.


关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now