Search code examples
javajunitcollectionsassertion

Junit: How to check if 2 collections are equals with elements in different order?


I am trying to check if two collections are the same using Assertions. They are supposed to be the same, even if their elements are in different order.

Here is my method for checking equality:

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

Example collections:

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

So my collections look like this:

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

Test:

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

Output:

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

How do I make my tests succeed for all of my test collections?


Solution

  • To check equality of two collections, the size of the two collections should be compared, next a difference may be calculated by removing elements in collection c2 from the copy of collection c1 and if it's empty the collections are equal:

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

    Such method allows to compare different types of collections (e.g. List to Set)

    Then common assertTrue / assertFalse may be used in tests:

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

    Update Collection::removeAll may produce incorrect result in the case when the collections contain the same elements with different frequencies. e.g. List.of (1, 2, 2, 2) and List.of (1, 2, 1, 2), so it was replaced with iterative remove.