关于java:AssertEquals 2列表忽略顺序

AssertEquals 2 Lists ignore order

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

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

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

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

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

我使用Hamcrest 1.3,JUnit 4.11,Mockito 1.9.5。


正如您提到使用Hamcrest一样,我会选择Matchers之一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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()));
    }

}


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

1
2
assertTrue(first.size() == second.size() &&
    first.containsAll(second) && second.containsAll(first));


这是一种避免二次复杂性的解决方案(多次遍历列表)。这使用Apache Commons CollectionUtils类来创建每个项目的映射,以将其映射到列表中的频率计数本身。然后,它仅比较两个Map。

1
2
3
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


1
2
3
    Collections.sort(excepted);
    Collections.sort(actual);
    assertEquals(excepted,actual);

我参加聚会晚了,但这是我仅使用Junit的解决方案。任何想法都欢迎。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
}

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

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


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

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


使用AssertJ,您需要containsExactlyInAnyOrder()containsExactlyInAnyOrderElementsOf()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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");
    }    
}

为了快速解决问题,我将同时检查两种方法:

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

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