是否有忽略 jest.js 中元素位置的数组相等匹配函数?

Posted

技术标签:

【中文标题】是否有忽略 jest.js 中元素位置的数组相等匹配函数?【英文标题】:Is there an Array equality match function that ignores element position in jest.js? 【发布时间】:2017-03-01 08:19:56 【问题描述】:

我知道.toEqual() 检查普通对象的所有字段是否相等:

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

这样就通过了。

但数组却不是这样:

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

在 jest 文档中似乎没有匹配器函数执行此操作,即测试两个数组的相等性,而不管它们的元素位置如何。我是否必须针对另一个数组中的所有元素测试一个数组中的每个元素,反之亦然?还是有别的办法?

【问题讨论】:

还是个问题,或许我们可以为 Jest 做个 PR? 【参考方案1】:

您可以使用this answer 中所述的集合与检查实际结果和期望的长度相结合。这将忽略元素位置并同时保护您免受重复元素的影响。

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

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

编辑:正如下面评论中所建议的,这仅适用于具有唯一值的数组。

【讨论】:

请注意,此解决方案仅适用于具有唯一值的数组。尝试将 ['pink wool', 'diorite', 'diorite'] 与 ['pink wool', 'pink wool', 'diorite'] 进行比较【参考方案2】:

如前所述,expect.arrayContaining 检查actual 数组是否包含expected 数组作为子集。 要检查等效性,可以

要么断言两个数组的长度相同(但这不会导致有用的失败消息) 或断言相反:expected 数组包含 actual 数组:
// 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));
;

这仍然存在一个问题,即当第一个断言中的测试失败时,只知道actual 中缺少的元素,而不是expected 中没有的额外元素。

【讨论】:

【参考方案3】:

检查内容和长度呢?

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

【讨论】:

【参考方案4】:

另一种方法是使用来自jest-community/jest-extended 的自定义匹配器.toIncludeSameMembers()

自述文件中给出的示例

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

仅为一个匹配器导入库可能没有意义,但他们有很多其他有用的匹配器,我发现它们很有用。

【讨论】:

【参考方案5】:

仍在进行中,但这应该可以正常工作,错误消息可能不清楚:

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

然后像这样使用它:

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

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

我们循环遍历每个值并将其从接收到的数组中删除,以便我们可以利用 Jest 提供的非对称匹配。如果我们只是想做直接等价,这可以简化为只比较两个排序的数组。

注意:此解决方案使用来自lodashfindIndexisEqual

【讨论】:

【参考方案6】:

如果您想在 JEST 中比较两个数组,请使用波纹管模型。

官方链接: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));
);

【讨论】:

小心——顾名思义——检查数组是否包含另一个数组中的项目。如果您检查 expect(['a','b']).toEqual(expect.arrayContaining(['a'])); 是否如其他答案中所述,则测试将通过。【参考方案7】:

没有内置方法来比较数组而不比较顺序,但您可以在比较之前使用.sort()简单地对数组进行排序:

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

您可以查看this fiddle中的示例。

【讨论】:

如果您使用对象数组,这将不起作用。因为对象将被转换为 [Object object] 并且不会被排序。你的比较可能仍然失败。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…. 是的,我的意思是你必须根据数据类型为排序函数提供一个合适的比较器。【参考方案8】:

如果您没有对象数组,那么您可以在比较之前简单地使用sort() 函数进行排序。(在接受的答案中提到):

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

但是,如果您有对象数组,在这种情况下sort 函数将不起作用,就会出现问题。在这种情况下,您需要提供自定义排序功能。 示例:

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)

希望有一天它对某人有所帮助。

【讨论】:

更紧凑的方式: const sortByKey = (a, b) => (a.key b.key ? 1 : 0)【参考方案9】:

这并不能完全回答这个问题,但仍然可以帮助那些最终通过谷歌搜索到这里的人:

如果您只关心数组的子集是否包含某些元素,请使用 expect.arrayContaining() https://jestjs.io/docs/en/expect#expectarraycontainingarray

例如,

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

【讨论】:

如果数组有附加值,这不会失败。重点是检查是否相等。 您始终可以测试数组的长度,以确保此测试符合预期,而不是预期的子集。【参考方案10】:

将元素放入一个集合中。 Jest 知道如何匹配这些。

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

【讨论】:

这是在 Jest 23.0.0 中引入的,因此不适用于以前的版本。 这将忽略重复值,这可能不是您想要的。 ["a", "a", "b"] 将匹配 ["a", "b"] 不要使用它,因为重复的元素不会被计算在内。在你摔断头之前避免 我强烈建议您删除此答案,因为它几乎在所有方面都不如接受的答案。我唯一能想象你的答案更好的是我们有很多重复的情况,因此之后的比较可能会更快。如果这是你想说的,请说得更清楚。如果不是,您的回答会非常具有误导性,并且可能会在应用程序中造成很多损害,因为它看起来很好并且会一直工作,直到它不再起作用,那么损害可能是巨大的。

以上是关于是否有忽略 jest.js 中元素位置的数组相等匹配函数?的主要内容,如果未能解决你的问题,请参考以下文章

二分查找2在排序数组中查找元素的第一个和最后一个位置(medium)

二分查找2在排序数组中查找元素的第一个和最后一个位置(medium)

JS比较两个数组是否相等 是否拥有相同元素

python如何判断两个数组完全相等?

.封装函数, 在数组 arr 中,查找值与 item 相等的元素出现的所有位置.

每日日报