Reputation: 2969
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?
Upvotes: 219
Views: 185350
Reputation: 1875
As already mentioned expect.arrayContaining
checks if the actual
array contains the expected
array as a subset.
To check for equivalence one may
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
.
Upvotes: 69
Reputation: 12858
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"]));
Upvotes: 23
Reputation: 20885
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)
Upvotes: 5
Reputation: 944
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.
Upvotes: 5
Reputation: 101
What about checking the content and the length?
expect(resultArray).toEqual(expect.arrayContaining(expectedArray));
expect(resultArray.length).toEqual(expectedArray.length);
Upvotes: 10
Reputation: 13767
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.
Upvotes: 23
Reputation: 2222
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
.
Upvotes: 1
Reputation: 1724
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));
});
Upvotes: 5
Reputation: 4818
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.
Upvotes: 228
Reputation: 2892
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.
Upvotes: 1
Reputation: 13681
Put the elements into a set. Jest knows how to match these.
expect(new Set(["pink wool", "diorite"])).toEqual(new Set(["diorite", "pink wool"]));
Upvotes: 38