AssertEquals 2 Lists ignore order

2019-01-08 20:28发布

问题:

That should be really simple question I believe. But somehow I can't find answer in Google.

Assume that I have 2 Lists of Strings. First contains "String A" and "String B", second one contains "String B" and "String A" (notice difference in order). I want to test them with JUnit to check whether they contains exactly the same Strings.

Is there any assert that checks equality of Strings that ignore order? For given example org.junit.Assert.assertEquals throws AssertionError

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

Work around is to sort Lists firstly and then pass them to assertion. But I want my code to be as simple and clean as possible.

I use Hamcrest 1.3, JUnit 4.11, Mockito 1.9.5.

回答1:

As you mention that you use Hamcrest, I would pick one of the collection Matchers

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()));
    }

}


回答2:

You can use List.containsAll to check that the first list contains every element from the second one, and vice versa.

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


回答3:

Here's a solution that avoids quadratic complexity (iterating over the lists multiple times). This uses the Apache Commons CollectionUtils class to create a Map of each item to a frequency count itself in the list. It then simply compares the two Maps.

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

I also just spotted CollectionUtils.isEqualCollection that claims to do exactly what is being requested here...

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



回答4:

Note that solution by Roberto Izquierdo has quadratic complexity in general. Solution on HashSets always has linear complexity:

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


回答5:

You can use ListAssert that comes in junt-addons jar.

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


回答6:

For a quick fix I would check both ways:

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

And trying with a situation where the number of the same elements is different (e.g. 1, 1, 2 and 1, 2, 2) I didn't get false positives.



回答7:

Im late to the party but here's my solution using Junit only. Any thoughts are welcome.

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