【问题标题】:Junit: How to check if 2 collections are equals with elements in different order?Junit:如何检查 2 个集合是否与不同顺序的元素相等?
【发布时间】:2021-06-25 01:28:36
【问题描述】:

我正在尝试使用断言检查两个集合是否相同。它们应该是相同的,即使它们的元素顺序不同。

这是我检查相等性的方法:

 public static <T> void assertCollectionsAreEquals (Collection<T> expected, Collection<T> actual, String message) {
        Assertions.assertEquals(expected, actual, message);    
        }

示例集合:

        Collection <Integer> one = new ArrayList<Integer>();
        Collection <Integer> two = new ArrayList<Integer>();
        Collection <Integer> three = new ArrayList<Integer>();

        one.add(1);
        one.add(2);
        two.add(1);
        two.add(2);
        three.add(2);
        three.add(1);

所以我的收藏看起来像这样:

One:[1, 2]
Two:[1, 2]
Three:[2, 1]

测试:

assertCollectionsAreEquals(one, two, "Not Equals");
assertCollectionsAreEquals(one, three, "Not Equals");

输出:

Exception in thread "main" org.opentest4j.AssertionFailedError: Not Equals ==> expected: <[1, 2]> but was: <[2, 1]>

如何使我的所有测试集合的测试成功?

【问题讨论】:

  • 先对集合进行排序,然后进行比较。
  • “它们应该是一样的,即使顺序不同”,那么,Set的广告是什么?
  • equals() 的概念没有在层次结构的那个级别定义,可用的定义完全取决于特定集合的合同。您的方法接受任何Collection,但您不能将其应用于List&lt;&gt;Set&lt;&gt;。在回答问题之前,您需要考虑实际的实现及其行为。
  • 您可能需要考虑一个不同的断言库,它具有比 junit 内置的断言集更丰富的断言集。例如在 assertJ 你可以使用assertThat(one).containsExactlyInAnyOrder(two)

标签: java junit collections assertion


【解决方案1】:

要检查两个集合的相等性,应该比较两个集合的大小,接下来可以通过从集合c1 的副本中删除集合c2 中的元素来计算差异,如果它为空,则集合相等:

public static <T> boolean areCollectionsEqual(Collection<T> c1, Collection<T> c2) {
    if (c1 == c2) return true;
    
    if (c1 == null || c2 == null || c1.size() != c2.size()) {
        return false;
    }
    
    Collection<T> tmp = new ArrayList<>(c1);
    for (T item : c2) {
        if (!tmp.remove(item)) {
            return false;
        }
    }
    
    return tmp.isEmpty();
}

这种方法允许比较不同类型的集合(例如 ListSet

那么常见的assertTrue/assertFalse可以用于测试:

public static <T> void assertCollectionsAreEqual(Collection<T> expected, Collection<T> actual, String message) {
    Assertions.assertTrue(areCollectionsEqual(expected, actual), message);    
}

public static <T> void assertCollectionsAreNotEqual(Collection<T> expected, Collection<T> actual, String message) {
    Assertions.assertFalse(areCollectionsEqual(expected, actual), message);    
}

更新 Collection::removeAll 在集合包含不同频率的相同元素的情况下可能会产生不正确的结果。例如List.of (1, 2, 2, 2)List.of (1, 2, 1, 2),所以它被替换为迭代 remove

【讨论】:

  • 两个问题:首先,这个问题没有明确说明,必须考虑比较不同类型的集合是否相等意味着什么。其次,您的解决方案可能会产生不正确的结果。考虑一个预期 List{1,2,3} 和一个实际 List{1,2,3,4}removeAll(Collection c) 删除 this 中的每个元素 e,其中 c.contains(e) 返回 true。因此,即使列表绝对不相等,在expected.removeAll(actual) 之后,结果为空,您将返回true
  • 主要问题是 a.removeAll(b)b.removeAll(a) 都必须进行测试,并且只有当两者都导致空列表时,集合才相等。
  • {1, 2, 3}{1, 2, 3, 4} 大小不相等;但是,removeAll 似乎不适合这项任务,我已将其替换为迭代的 remove
【解决方案2】:

在 JUnit 5 中没有现成的断言方法来实现这一点。您可以改用 Hamcrest:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;

assertThat("Not Equals", one, containsInAnyOrder(two.toArray()));
assertThat("Not Equals", one, containsInAnyOrder(three.toArray()));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-05
    • 2012-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多