146

I'm trying to compare 2 lists:

assertThat(actual.getList(), is(Matchers.containsInAnyOrder(expectedList)));

But idea

java: no suitable method found for assertThat(java.util.List<Agent>,org.hamcrest.Matcher<java.lang.Iterable<? extends model.Agents>>)
method org.junit.Assert.<T>assertThat(T,org.hamcrest.Matcher<T>) is not applicable
  (no instance(s) of type variable(s) T exist so that argument type org.hamcrest.Matcher<java.lang.Iterable<? extends model.Agents>> conforms to formal parameter type org.hamcrest.Matcher<T>)
method org.junit.Assert.<T>assertThat(java.lang.String,T,org.hamcrest.Matcher<T>) is not applicable
  (cannot instantiate from arguments because actual and formal argument lists differ in length)

How should I write it?

Joe
  • 29,416
  • 12
  • 68
  • 88
xander27
  • 3,014
  • 8
  • 30
  • 42

7 Answers7

203

If you want to assert that the two lists are identical, don't complicate things with Hamcrest:

assertEquals(expectedList, actual.getList());

If you really intend to perform an order-insensitive comparison, you can call the containsInAnyOrder varargs method and provide values directly:

assertThat(actual.getList(), containsInAnyOrder("item1", "item2"));

(Assuming that your list is of String, rather than Agent, for this example.)

If you really want to call that same method with the contents of a List:

assertThat(actual.getList(), containsInAnyOrder(expectedList.toArray(new String[expectedList.size()]));

Without this, you're calling the method with a single argument and creating a Matcher that expects to match an Iterable where each element is a List. This can't be used to match a List.

That is, you can't match a List<Agent> with a Matcher<Iterable<List<Agent>>, which is what your code is attempting.

Joe
  • 29,416
  • 12
  • 68
  • 88
  • +1 for the "If you really want to call that same method with the contents of a List". Sadly I couldn't get that solved myself. Specially that there is a constructor that takes in a collection. – Eyad Ebrahim Oct 06 '14 at 18:30
  • 4
    @Tim Not quite; `containsInAnyOrder` requires that *all* elements are present, so that first assertion will fail. See `hasItems` if you want to check that *at least* those elements are present. – Joe Jul 07 '15 at 10:37
  • 4
    If you use containsInAnyOrder, you should first make sure both lists have the same size... If `actual.getList()` happens to contain `"item1", "item3", "item2"`, the test will pass and maybe you want to make sure it only contains the items listed... In that case you could use `assertThat(actual.getList().size(), equalTo(2));` before the containsInAnyOrder, this way you make sure both lists have the same contents. – Martin Nov 20 '15 at 15:31
  • 2
    @Martin you're thinking of `hasItems`. The extra check is unnecessary here. See the comment to Tim above, and also [How do Hamcrest's hasItems, contains and containsInAnyOrder differ?](http://stackoverflow.com/q/33840531/733345) – Joe Nov 21 '15 at 06:43
  • 4
    **Kotlin users**: don't forget to add the spread operator (`*expectedList.toTypedArray()`) when passing an array as varargs! – James Bowman Dec 10 '18 at 19:39
  • Can I draw your attention to https://stackoverflow.com/questions/68107741/hamcrest-matchers-containsinanyorder-does-not-work I tried the above and was dealing with a significantly more complex list of items to match - the answer here resolved me issue – MarkAddison Oct 13 '22 at 10:15
82
List<Long> actual = Arrays.asList(1L, 2L);
List<Long> expected = Arrays.asList(2L, 1L);
assertThat(actual, containsInAnyOrder(expected.toArray()));

Shorter version of @Joe's answer without redundant parameters.

Nelson Tatius
  • 7,693
  • 8
  • 47
  • 70
45

To complement @Joe's answer:

Hamcrest provides you with four main methods to match a list:

contains Checks for matching all the elements considering the order. If the list has more or fewer elements, it will fail

containsInAnyOrder Checks for matching all the elements, and it doesn't matter the order. If the list has more or fewer elements, will fail

hasItems Checks just for the specified objects it doesn't matter if the list has more

hasItem Checks just for one object it doesn't matter if the list has more

All of them can receive a list of objects and use the equals method for comparison or can be mixed with other matchers like @borjab mentioned:

assertThat(myList , contains(allOf(hasProperty("id", is(7L)), 
                                   hasProperty("name", is("testName1")),
                                   hasProperty("description", is("testDesc1"))),
                             allOf(hasProperty("id", is(11L)), 
                                   hasProperty("name", is("testName2")),
                                   hasProperty("description", is("testDesc2")))));

http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#contains(E...) http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#containsInAnyOrder(java.util.Collection) http://hamcrest.org/JavaHamcrest/javadoc/1.3/org/hamcrest/Matchers.html#hasItems(T...)

rvazquezglez
  • 2,284
  • 28
  • 40
20

With existing Hamcrest libraries (as of v.2.0.0.0) you are forced to use Collection.toArray() method on your Collection in order to use containsInAnyOrder Matcher. Far nicer would be to add this as a separate method to org.hamcrest.Matchers:

public static <T> org.hamcrest.Matcher<java.lang.Iterable<? extends T>> containsInAnyOrder(Collection<T> items) {
    return org.hamcrest.collection.IsIterableContainingInAnyOrder.<T>containsInAnyOrder((T[]) items.toArray());
}

Actually I ended up adding this method to my custom test library and use it to increase readability of my test cases (due to less verbosity).

yvolk
  • 2,391
  • 3
  • 21
  • 26
  • 4
    Nice one, I will use this helper. The assert message here is more informative: it names the missing items one-by-one, not just: the list should be elem1, elem2, .. elem99, but I got elem1, elem2, ..., elem98 -- good luck finding the missing one. – pihentagy Sep 07 '16 at 13:23
5

For list of objects you may need something like this:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.Matchers.is;

@Test
@SuppressWarnings("unchecked")
public void test_returnsList(){

    arrange();
  
    List<MyBean> myList = act();
    
    assertThat(myList , contains(allOf(hasProperty("id",          is(7L)), 
                                       hasProperty("name",        is("testName1")),
                                       hasProperty("description", is("testDesc1"))),
                                 allOf(hasProperty("id",          is(11L)), 
                                       hasProperty("name",        is("testName2")),
                                       hasProperty("description", is("testDesc2")))));
}

Use containsInAnyOrder if you do not want to check the order of the objects.

P.S. Any help to avoid the warning that is suppresed will be really appreciated.

Community
  • 1
  • 1
borjab
  • 11,149
  • 6
  • 71
  • 98
4

Make sure that the Objects in your list have equals() defined on them. Then

assertThat(generatedList, is(equalTo(expectedList)));

works.

Ahmed Ashour
  • 5,179
  • 10
  • 35
  • 56
Jim Jarrett
  • 458
  • 1
  • 6
  • 12
-6

To compare two lists with the order preserved (strict order) use.

assertThat(actualList, contains("item1","item2"));

If we want to compare without a specific order we can use below command

assertThat(collection, containsInAnyOrder("item1","item2"));
Shravan Ramamurthy
  • 3,896
  • 5
  • 30
  • 44