期望数组相等而忽略顺序

176

Jasmine是否有一种测试方法,可以测试2个数组是否包含相同的元素,但不一定按相同的顺序排列?例如:

array1 = [1,2,3];
array2 = [3,2,1];

expect(array1).toEqualIgnoreOrder(array2);//should be true

37
希望(array1.sort())能够等于(array2.sort())。 - raina77ow
@raina77ow 我猜那也可以。 - David says Reinstate Monica
1
我应该把这个作为答案吗? - raina77ow
2
@raina77ow 当涉及到对象数组时,情况会变得有些复杂。如果Jasmine能够提供一些开箱即用的功能就好了。 - David says Reinstate Monica
2
我在Jasmine本身中没有发现任何很好的东西,所以实际上我将lodash(或者你可以使用underscore/其他js集合库)引入到我的测试项目中,用于处理像这样的事情。 - ktharsis
1
我最喜欢@raina77ow的答案。唯一的注意点是sort会改变输入数组,尽管这可能不是什么大问题。如果它引起了问题,你可以使用[...array1].sort() - jimmyfever
15个回答

111

编辑

Jasmine 2.8 添加了 arrayWithExactContents,如果实际值是一个包含样本中所有元素的数组(顺序无关紧要),则会成功。

请参见 keksmasta 的答案


原始(过时)答案

如果只是整数或其他基本类型,你可以在比较之前使用sort()对它们进行排序。

expect(array1.sort()).toEqual(array2.sort());
如果是对象,则与 map() 函数结合使用,提取将被比较的标识符。
array1 = [{id:1}, {id:2}, {id:3}];
array2 = [{id:3}, {id:2}, {id:1}];

expect(array1.map(a => a.id).sort()).toEqual(array2.map(a => a.id).sort());

14
只要两个数组的顺序相同,这种情况下它似乎并不重要。只要顺序一致就可以了。 - Coloured Panda
5
公正的观点。值得注意的是,“sort”在原地排序(in-place),也就是说,它会改变被调用的实例。 - Shmiddty
4
答案中的对象部分实际上不会验证对象是否匹配,因为它只是比较了映射数组。你不需要使用map,sort函数有一个可选的函数参数可以用来进行比较。 - slifty
2
这不应该是第一个答案,因为它已经过时了。请参见下面关于arrayWithExactContents的答案。 - Shachar Har-Shuv
@ShacharHar-Shuv 谢谢,我加了一条注释表示现在更喜欢另一种答案。虽然我不确定比较对象时它是如何工作的。 - Coloured Panda
显示剩余4条评论

54
你可以使用jest的标准函数expect.arrayContaining(array):
  const expected = ['Alice', 'Bob'];
  it('matches even if received contains additional elements', () => {
    expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
  });

34
这个答案不适用于这种情况,因为问题中的两个数组应该是相等的,只是顺序有所不同。 - mjarraya
2
这是正确的答案。请将其标记为正确答案。@mjarraya 只需测试两个数组的长度是否相同,您就可以了。 - enanone
5
这句话的翻译是:由于不完整,它不应被标记为正确答案!@enanone - mjarraya
3
有两个问题:
  1. 需要检查长度。 expect(actual.length).toEqual(expected.length);
  2. 应该使用 jasmine.arrayContaining。 https://jasmine.github.io/2.6/introduction#section-Partial_Array_Matching_with_%3Ccode%3Ejasmine.arrayContaining%3C/code%3E
- zeroliu
14
这个代码 expect([1, 2, 3]).toEqual(expect.arrayContaining([3, 3, 3])) 仍然会通过测试,因此不应该使用它。请注意,这只是一个翻译,没有包括任何额外的解释或信息。 - Jordan Burnett

51

jasmine 2.8及以上版本拥有

jasmine.arrayWithExactContents()

期望数组包含列出的元素,顺序不限。

array1 = [1,2,3];
array2 = [3,2,1];
expect(array1).toEqual(jasmine.arrayWithExactContents(array2))

请参阅 https://jasmine.github.io/api/3.4/jasmine.html


23

jest-extended软件包为我们提供了一些断言来简化测试,它更简洁,并且对于失败的测试,错误更明确。

对于这种情况,我们可以使用toIncludeSameMembers

expect([{foo: "bar"}, {baz: "qux"}]).toIncludeSameMembers([{baz: "qux"}, {foo: "bar"}]);

13

简单...

array1 = [1,2,3];
array2 = [3,2,1];

expect(array1).toEqual(jasmine.arrayContaining(array2));

13
好的回答!你还需要检查长度是否相等,否则在[1,2,3,4]和[3,2,1]上会产生误报。 - Kristian Hanekamp

10
// check if every element of array2 is element of array1
// to ensure [1, 1] !== [1, 2]
array2.forEach(x => expect(array1).toContain(x))

// check if every element of array1 is element of array2
// to ensure [1, 2] !== [1, 1]
array1.forEach(x => expect(array2).toContain(x))

// check if they have equal length to ensure [1] !== [1, 1]
expect(array1.length).toBe(array2.length)

2
使用.forEach代替.map可以节省时间和大量内存。 - Darkhogg
1
不幸的是,即使它们不同,以下数组也会通过:array1 = [1, 2]array2 = [1, 1] - redbmk
2
很好的发现 @redbmk,我加了一个检查,谢谢! - Jannic Beck
我认为还存在一个问题 - 如果数组是 [1,1,2][1,2,2] 呢?也许可以为每个数组使用 Map 或其他什么东西?例如,对于两个数组都可以使用 array1.reduce((map, item) => { map.set(item, (map.get(item) || 0) + 1)), new Map()),然后循环遍历它们并检查数量是否相同?这似乎需要很多迭代,但会更彻底。 - redbmk
控制数组中的排除项可以使用(找到元素后删除,然后最后检查长度是否为0),但在常规情况下不值得这样做。 - lifecoder

2
集合忽略顺序,因此可以在这里使用。
expect(new Set(a)).toEqual(new Set(b))

1
//Compare arrays without order
//Example
//a1 = [1, 2, 3, 4, 5]
//a2 = [3, 2, 1, 5, 4]
//isEqual(a1, a2) -> true
//a1 = [1, 2, 3, 4, 5];
//a2 = [3, 2, 1, 5, 4, 6];
//isEqual(a1, a2) -> false


function isInArray(a, e) {
  for ( var i = a.length; i--; ) {
    if ( a[i] === e ) return true;
  }
  return false;
}

function isEqArrays(a1, a2) {
  if ( a1.length !== a2.length ) {
    return false;
  }
  for ( var i = a1.length; i--; ) {
    if ( !isInArray( a2, a1[i] ) ) {
      return false;
    }
  }
  return true;
}

1

这里使用了 forEach 方法来比较每个数组中的每个元素,根据一个唯一的对象属性(这里使用了 status)。

const expected = [ 
     { count: 1, status: "A" },
     { count: 3, status: "B" },
];
result.forEach((item) => {
     expect(expected.find(a => a.status === item.status)).toEqual(item);
})

这个答案只处理所有结果都在expected中找到的情况。它只断言resultexpected的一个子集。你需要反过来断言所有expected的项目都在result中。 - undefined

0

这里有一个适用于任意数量数组的解决方案。

https://gist.github.com/tvler/cc5b2a3f01543e1658b25ca567c078e4

const areUnsortedArraysEqual = (...arrs) =>
  arrs.every((arr, i, [first]) => !i || arr.length === first.length) &&
  arrs
    .map(arr =>
      arr.reduce(
        (map, item) => map.set(item, (map.get(item) || 0) + 1),
        new Map(),
      ),
    )
    .every(
      (map, i, [first]) =>
        !i ||
        [...first, ...map].every(([item]) => first.get(item) === map.get(item)),
    );

一些测试(对于具有相同值的多个项的数组,某些答案不予考虑,因此[1、2、2]和[1、2]将错误地返回true)

[1, 2] true
[1, 2], [1, 2] true
[1, 2], [1, 2], [1, 2] true
[1, 2], [2, 1] true
[1, 1, 2], [1, 2, 1] true
[1, 2], [1, 2, 3] false
[1, 2, 3, 4], [1, 2, 3], [1, 2] false
[1, 2, 2], [1, 2] false
[1, 1, 2], [1, 2, 2] false
[1, 2, 3], [1, 2], [1, 2, 3] false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接