【问题标题】:AssertEquals 2 Lists ignore orderAssertEquals 2 列表忽略顺序
【发布时间】: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)); 也很干净。
  • @SudoRahul - 如果您不想通过删除所有来修改列表怎么办?
  • @BoratSagdiyev - 由于这不是 OP 的限制,我建议这样做。但如果这是一个限制条件,那么这个问题的公认答案就可以解决手头的问题。

标签: java arrays unit-testing junit assertion


【解决方案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()));
    }
    
}

【讨论】:

  • 另请参阅我的回答 stackoverflow.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;
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2021-10-31
              • 1970-01-01
              • 1970-01-01
              • 2021-08-31
              • 2019-05-08
              • 2015-04-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多