[java] Assert equals between 2 Lists in Junit

How can I make an equality assertion between lists in a JUnit test case? Equality should be between the content of the list.

For example:

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
List<String> numbers3 = Arrays.asList("one", "two", "four"); 

// numbers should be equal to numbers2
//numbers should not be equal to numbers3

This question is related to java unit-testing collections junit

The answer is


Don't reinvent the wheel!

There's a Google Code library that does this for you: Hamcrest

[Hamcrest] Provides a library of matcher objects (also known as constraints or predicates) allowing 'match' rules to be defined declaratively, to be used in other frameworks. Typical scenarios include testing frameworks, mocking libraries and UI validation rules.


If you don't care about the order of the elements, I recommend ListAssert.assertEquals in junit-addons.

Link: http://junit-addons.sourceforge.net/

For lazy Maven users:

    <dependency>
        <groupId>junit-addons</groupId>
        <artifactId>junit-addons</artifactId>
        <version>1.4</version>
        <scope>test</scope>
    </dependency>

For junit4! This question deserves a new answer written for junit5.

I realise this answer is written a couple years after the question, probably this feature wasn't around then. But now, it's easy to just do this:

@Test
public void test_array_pass()
{
  List<String> actual = Arrays.asList("fee", "fi", "foe");
  List<String> expected = Arrays.asList("fee", "fi", "foe");

  assertThat(actual, is(expected));
  assertThat(actual, is(not(expected)));
}

If you have a recent version of Junit installed with hamcrest, just add these imports:

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

http://junit.org/junit4/javadoc/latest/org/junit/Assert.html#assertThat(T, org.hamcrest.Matcher)

http://junit.org/junit4/javadoc/latest/org/hamcrest/CoreMatchers.html

http://junit.org/junit4/javadoc/latest/org/hamcrest/core/Is.html


I don't this the all the above answers are giving the exact solution for comparing two lists of Objects. Most of above approaches can be helpful in following limit of comparisons only - Size comparison - Reference comparison

But if we have same sized lists of objects and different data on the objects level then this comparison approaches won't help.

I think the following approach will work perfectly with overriding equals and hashcode method on the user-defined object.

I used Xstream lib for override equals and hashcode but we can override equals and hashcode by out won logics/comparison too.

Here is the example for your reference

    import com.thoughtworks.xstream.XStream;

    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.List;

    class TestClass {
      private String name;
      private String id;

      public void setName(String value) {
        this.name = value;
      }

      public String getName() {
        return this.name;
      }

      public String getId() {
        return id;
      }

      public void setId(String id) {
        this.id = id;
      }

      /**
       * @see java.lang.Object#equals(java.lang.Object)
       */
      @Override
      public boolean equals(Object o) {
        XStream xstream = new XStream();
        String oxml = xstream.toXML(o);
        String myxml = xstream.toXML(this);

        return myxml.equals(oxml);
      }

      /**
       * @see java.lang.Object#hashCode()
       */
      @Override
      public int hashCode() {
        XStream xstream = new XStream();
        String myxml = xstream.toXML(this);
        return myxml.hashCode();
      }
    }

    public class XstreamCompareTest {
      public static void main(String[] args) throws ParseException {
      checkObjectEquals();
}

      private static void checkObjectEquals() {
        List<TestClass> testList1 = new ArrayList<TestClass>();
        TestClass tObj1 = new TestClass();
        tObj1.setId("test3");
        tObj1.setName("testname3");
        testList1.add(tObj1);

        TestClass tObj2 = new TestClass();
        tObj2.setId("test2");
        tObj2.setName("testname2");
        testList1.add(tObj2);

        testList1.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        List<TestClass> testList2 = new ArrayList<TestClass>();
        TestClass tObj3 = new TestClass();
        tObj3.setId("test3");
        tObj3.setName("testname3");
        testList2.add(tObj3);

        TestClass tObj4 = new TestClass();
        tObj4.setId("test2");
        tObj4.setName("testname2");
        testList2.add(tObj4);

        testList2.sort((TestClass t1, TestClass t2) -> t1.getId().compareTo(t2.getId()));

        if (isNotMatch(testList1, testList2)) {
          System.out.println("The list are not matched");
        } else {
          System.out.println("The list are matched");
        }

      }

      private static boolean isNotMatch(List<TestClass> clist1, List<TestClass> clist2) {
        return clist1.size() != clist2.size() || !clist1.equals(clist2);
      }
    }

The most important thing is that you can ignore the fields by Annotation (@XStreamOmitField) if you don't want to include any fields on the equal check of Objects. There are many Annotations like this to configure so have a look deep about the annotations of this lib.

I am sure this answer will save your time to identify the correct approach for comparing two lists of objects :). Please comment if you see any issues on this.


List<Integer> figureTypes = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2
                            ));

List<Integer> figureTypes2 = new ArrayList<Integer>(
                           Arrays.asList(
                                 1,
                                 2));

assertTrue(figureTypes .equals(figureTypes2 ));

assertEquals(expected, result); works for me. Since this function gets two objects, you can pass anything to it.

public static void assertEquals(Object expected, Object actual) {
    AssertEquals.assertEquals(expected, actual);
}

For JUnit 5

you can use assertIterableEquals :

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");

Assertions.assertIterableEquals(numbers, numbers2);

or assertArrayEquals and converting lists to arrays :

List<String> numbers = Arrays.asList("one", "two", "three");
List<String> numbers2 = Arrays.asList("one", "two", "three");
Assertions.assertArrayEquals(numbers.toArray(), numbers2.toArray());

Don't transform to string and compare. This is not good for perfomance. In the junit, inside Corematchers, there's a matcher for this => hasItems

List<Integer> yourList = Arrays.asList(1,2,3,4)    
assertThat(yourList, CoreMatchers.hasItems(1,2,3,4,5));

This is the better way that I know of to check elements in a list.


if you don't want to build up an array list , you can try this also

@Test
public void test_array_pass()
{
  List<String> list = Arrays.asList("fee", "fi", "foe");
  Strint listToString = list.toString();
  Assert.assertTrue(listToString.contains("[fee, fi, foe]"));   // passes  
}

You can use assertEquals in junit.

import org.junit.Assert;   
import org.junit.Test;

    @Test
    public void test_array_pass()
    {
        List<String> actual = Arrays.asList("fee", "fi", "foe");
        List<String> expected = Arrays.asList("fee", "fi", "foe");
        Assert.assertEquals(actual,expected);
    }

If the order of elements is different then it will return error.

If you are asserting a model object list then you should override the equals method in the specific model.

    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj instanceof ModelName) {
            ModelName other = (ModelName) obj;
            return this.getItem().equals(other.getItem()) ;
        }
        return false;
    }

assertEquals(Object, Object) from JUnit4/JUnit 5 or assertThat(actual, is(expected)); from Hamcrest proposed in the other answers will work only as both equals() and toString() are overrided for the classes (and deeply) of the compared objects.

It matters because the equality test in the assertion relies on equals() and the test failure message relies on toString() of the compared objects.
For built-in classes such as String, Integer and so for ... no problem as these override both equals() and toString(). So it is perfectly valid to assert List<String> or List<Integer> with assertEquals(Object,Object).
And about this matter : you have to override equals() in a class because it makes sense in terms of object equality, not only to make assertions easier in a test with JUnit.
To make assertions easier you have other ways.
As a good practice I favor assertion/matcher libraries.

Here is a AssertJ solution.

org.assertj.core.api.ListAssert.containsExactly() is what you need : it verifies that the actual group contains exactly the given values and nothing else, in order as stated in the javadoc.

Suppose a Foo class where you add elements and where you can get that.
A unit test of Foo that asserts that the two lists have the same content could look like :

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

@Test
void add() throws Exception { 
   Foo foo = new Foo();
   foo.add("One", "Two", "Three");
   Assertions.assertThat(foo.getElements())
             .containsExactly("One", "Two", "Three");
}

A AssertJ good point is that declaring a List as expected is needless : it makes the assertion straighter and the code more readable :

Assertions.assertThat(foo.getElements())
         .containsExactly("One", "Two", "Three");

But Assertion/matcher libraries are a must because these will really further.
Suppose now that Foo doesn't store Strings but Bars instances.
That is a very common need. With AssertJ the assertion is still simple to write. Better you can assert that the list content are equal even if the class of the elements doesn't override equals()/hashCode() while JUnit way requires that :

import org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple.tuple;
import org.junit.jupiter.api.Test;

@Test
void add() throws Exception {
    Foo foo = new Foo();
    foo.add(new Bar(1, "One"), new Bar(2, "Two"), new Bar(3, "Three"));
    Assertions.assertThat(foo.getElements())
              .extracting(Bar::getId, Bar::getName)
              .containsExactly(tuple(1, "One"),
                               tuple(2, "Two"),
                               tuple(3, "Three"));
}

I know there are already many options to solve this issue, but I would rather do the following to assert two lists in any oder:

assertTrue(result.containsAll(expected) && expected.containsAll(result))

You mentioned that you're interested in the equality of the contents of the list (and didn't mention order). So containsExactlyInAnyOrder from AssertJ is a good fit. It comes packaged with spring-boot-starter-test, for example.

From the AssertJ docs ListAssert#containsExactlyInAnyOrder:

Verifies that the actual group contains exactly the given values and nothing else, in any order. Example:

 // an Iterable is used in the example but it would also work with an array
 Iterable<Ring> elvesRings = newArrayList(vilya, nenya, narya, vilya);

 // assertion will pass
 assertThat(elvesRings).containsExactlyInAnyOrder(vilya, vilya, nenya, narya);

 // assertion will fail as vilya is contained twice in elvesRings.
 assertThat(elvesRings).containsExactlyInAnyOrder(nenya, vilya, narya);

This is a legacy answer, suitable for JUnit 4.3 and below. The modern version of JUnit includes a built-in readable failure messages in the assertThat method. Prefer other answers on this question, if possible.

List<E> a = resultFromTest();
List<E> expected = Arrays.asList(new E(), new E(), ...);
assertTrue("Expected 'a' and 'expected' to be equal."+
            "\n  'a'        = "+a+
            "\n  'expected' = "+expected, 
            expected.equals(a));

For the record, as @Paul mentioned in his comment to this answer, two Lists are equal:

if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.

See the JavaDocs of the List interface.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to unit-testing

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0 How to test the type of a thrown exception in Jest Unit Tests not discovered in Visual Studio 2017 Class Not Found: Empty Test Suite in IntelliJ Angular 2 Unit Tests: Cannot find name 'describe' Enzyme - How to access and set <input> value? Mocking HttpClient in unit tests Example of Mockito's argumentCaptor How to write unit testing for Angular / TypeScript for private methods with Jasmine Why is the Visual Studio 2015/2017/2019 Test Runner not discovering my xUnit v2 tests

Examples related to collections

Kotlin's List missing "add", "remove", Map missing "put", etc? How to unset (remove) a collection element after fetching it? How can I get a List from some class properties with Java 8 Stream? Java 8 stream map to list of keys sorted by values How to convert String into Hashmap in java How can I turn a List of Lists into a List in Java 8? MongoDB Show all contents from all collections Get nth character of a string in Swift programming language Java 8 Distinct by property Is there a typescript List<> and/or Map<> class/library?

Examples related to junit

Eclipse No tests found using JUnit 5 caused by NoClassDefFoundError for LauncherFactory How to resolve Unneccessary Stubbing exception JUnit 5: How to assert an exception is thrown? How do I mock a REST template exchange? Class Not Found: Empty Test Suite in IntelliJ Unable to find a @SpringBootConfiguration when doing a JpaTest Failed to load ApplicationContext (with annotation) Example of Mockito's argumentCaptor Mockito - NullpointerException when stubbing Method Spring jUnit Testing properties file