AssertEquals 2 列表忽略顺序

Posted

技术标签:

【中文标题】AssertEquals 2 列表忽略顺序【英文标题】:AssertEquals 2 Lists ignore order 【发布时间】:2014-05-13 11:49:41 【问题描述】:

我相信这应该是一个非常简单的问题。但不知何故,我无法在 Google 中找到答案。

假设我有 2 个字符串列表。第一个包含 "String A" 和 "String B",第二个包含 "String B" 和 "String A"(注意顺序不同)。我想用 JUnit 对它们进行测试,以检查它们是否包含 完全相同的字符串。

是否有任何断言检查忽略顺序的字符串的相等性?对于给定的示例 org.junit.Assert.assertEquals 抛出 AssertionError

java.lang.AssertionError: expected:<[String A, String B]> but was:<[String B, String A]>

解决方法是首先对列表进行排序,然后将它们传递给断言。但我希望我的代码尽可能简单干净。

我使用 Hamcrest 1.3JUnit 4.11Mockito 1.9.5

【问题讨论】:

list1.removeAll(list2) 应将list1 留空。我想你可以在此基础上得到你想要的。 containsAllremoveAllO(n²) 用于列表,同时对它们进行排序并测试是否相等是 O(nlogn)Collections.sort(list1); Collections.sort(list2); assertTrue(list1.equals(list2)); 也很干净。 Hamcrest compare collections的可能重复 @SudoRahul - 如果您不想通过删除所有来修改列表怎么办? @BoratSagdiyev - 由于这不是 OP 的限制,我建议这样做。但如果这是一个限制条件,那么这个问题的公认答案就可以解决手头的问题。 【参考方案1】:

正如您提到的,您使用 Hamcrest, 所以我会选择一个集合匹配器

import static org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder;
import static org.junit.Assert.assertThat;

public class CompareListTest 

    @Test
    public void compareList() 
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");
        
        assertThat("List equality without order", 
            actual, containsInAnyOrder(expected.toArray()));
    
    

【讨论】:

另请参阅我的回答 ***.com/a/38262680/297710,其中显示了如何改进 Hamcrest 匹配器并在每个使用 containsInAnyOrder 的断言中避免“.toArray()”【参考方案2】:

您可以使用 List.containsAll 和 JUnit 的 assertTrue 来检查第一个列表是否包含第二个列表中的每个元素,反之亦然。

assertEquals(expectedList.size(), actualList.size());
assertTrue(expectedList.containsAll(actualList));
assertTrue(actualList.containsAll(expectedList));

提示: 这不适用于列表中的重复项

【讨论】:

@kukis 这取决于,您要检查重复项吗? 是的,当然。 2 个给定的列表必须完全相同,只是忽略顺序。 @kukis 那么请查看 ZouZou 对您的问题的评论。 ..可能包括assertEquals(first.size(), second.size()) ..那么它应该可以按预期工作 这不适用于列表中的重复项。下面是一个示例来演示:List&lt;String&gt; list1 = Arrays.asList("a", "a", "b");List&lt;String&gt; list2 = Arrays.asList("a", "b", "b");assertEquals(list1.size(), list2.size());assertTrue(list1.containsAll(list2) &amp;&amp; list2.containsAll(list1)); 在这个示例中,两个断言都无法检测到列表实际上是不同的。 @AlexWorden 提到了 Apache Commons Collections 的 CollectionUtils.isEqualCollection(),在这个例子中,它正确地检测到集合不相等。【参考方案3】:

这是一种避免二次复杂性(多次迭代列表)的解决方案。这使用 Apache Commons CollectionUtils 类来创建每个项目的映射到列表中的频率计数本身。然后它只是比较两个地图。

Assert.assertEquals("Verify same metrics series",
    CollectionUtils.getCardinalityMap(expectedSeriesList),
    CollectionUtils.getCardinalityMap(actualSeriesList));

我也刚刚发现 CollectionUtils.isEqualCollection 声称完全按照这里的要求做......

https://commons.apache.org/proper/commons-collections/apidocs/index.html?org/apache/commons/collections4/CollectionUtils.html

【讨论】:

【参考方案4】:

使用 AssertJ,containsExactlyInAnyOrder()containsExactlyInAnyOrderElementsOf() 就是您所需要的:

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;

import java.util.Arrays;
import java.util.List;

public class CompareListTest 

    @Test
    public void compareListWithTwoVariables() 
        List<String> expected = Arrays.asList("String A", "String B");
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrderElementsOf(expected);
    

    @Test
    public void compareListWithInlineExpectedValues() 
        List<String> actual = Arrays.asList("String B", "String A");
        Assertions.assertThat(actual)
                  .containsExactlyInAnyOrder("String A", "String B");
        

【讨论】:

【参考方案5】:
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

【讨论】:

【参考方案6】:

我迟到了,但这是我仅使用 Junit 的解决方案。欢迎任何想法。

List<String> actual = new ArrayList<>();
actual.add("A");
actual.add("A");
actual.add("B");

List<String> expected = new ArrayList<>();
actual.add("A");
actual.add("B");
actual.add("B");

//Step 1: assert for size
assertEquals(actual.size(), expected.size());

//Step 2: Iterate
for(String e: expected)
    assertTrue(actual.contains(e));
    actual.remove(e);

【讨论】:

【参考方案7】:

请注意,Roberto Izquierdo 的解决方案通常具有二次复杂度。 HashSets 的解决方案总是具有线性复杂度:

assertTrue(first.size() == second.size() &&
        new HashSet(first).equals(new HashSet(second)));

【讨论】:

这种方法行不通。如果第一个是 ("String A") 而第二个是 ("String A", "String A") 它们不是同一个列表。 您无法检查大小。如果第一个是("s1", "s2", "s3" ,"s1"),第二个是("s2", "s1", "s3" ,"s2");,它们不是同一个列表。 @ZouZou 接受的解决方案有同样的问题。您提出了唯一真正正确的解决方案。如果你回答我会投票赞成。 @ZouZou 它们不是同一个列表,但它们包含完全相同的字符串。 OP,澄清?另外,让它成为一个答案,我也会投票赞成:) 没想到。 这对于所有情况(“A”、“A”、“B”)仍然不正确,将比较等于(“A”、“B”、“B”)【参考方案8】:

为了快速修复,我会检查两种方式:

assertTrue(first.containsAll(second));
assertTrue(second.containsAll(first));

在尝试相同元素的数量不同(例如 1、1、2 和 1、2、2)的情况下,我没有得到误报。

【讨论】:

您的代码仍然失败。看这个例子 - @Test public void test1() List list1 = Arrays.asList("a", "a", "b"); List list2 = Arrays.asList("a", "b", "b"); Assert.assertTrue(list1.containsAll(list2)); Assert.assertTrue(list2.containsAll(list1)); 【参考方案9】:

您可以使用 junit-addons jar 中的ListAssert。

ListAssert.assertEquals(yourList, Arrays.asList(3, 4, 5));

【讨论】:

【参考方案10】:

看起来其他答案要么引用了第 3 方实用程序,要么不正确,要么效率低下。

这是 Java 8 中的 O(N) vanilla 解决方案。

public static void assertContainsSame(Collection<?> expected, Collection<?> actual)

    assert expected.size() == actual.size();

    Map<Object, Long> counts = expected.stream()
        .collect(Collectors.groupingBy(
                item -> item,
                Collectors.counting()));

    for (Object item : actual)
        assert counts.merge(item, -1L, Long::sum) != -1L;

【讨论】:

以上是关于AssertEquals 2 列表忽略顺序的主要内容,如果未能解决你的问题,请参考以下文章

学习单元测试常用的断言 assertTrue 和 assertEquals 一篇文章就够了

TestNg的忽略测试和超时测试

TestNg的忽略测试和超时测试

Grails投影忽略MongoDB的排序顺序

按字母顺序排序时忽略变音符号

数据库单元应忽略行的顺序