ChatGPT解决这个技术问题 Extra ChatGPT

Hamcrest compare collections

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?


J
Joe

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.


+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.
@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.
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 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?
Kotlin users: don't forget to add the spread operator (*expectedList.toTypedArray()) when passing an array as varargs!
N
Nelson Tatius
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.


r
rvazquezglez

To complement @Joe's answer:

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

contains Checks for matching all the elements taking in count the order, if the list has more or less elements, it will fail

containsInAnyOrder Checks for matching all the elements and it doesn't matter the order, if the list has more or less 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 equals method for comparation 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...)


Great decision in case list items are not of primitive type.
Is there a type safe way of doing this?
y
yvolk

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


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.
C
Community

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.


A
Ahmed Ashour

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

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

works.


S
Shravan Ramamurthy

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

This does not answer the question.
It partially does.
@rvazquezglez What do you mean? Why do you say that? The result of the method is right in my environment.
@niaomingjian The code is checking that the actualList contains the elements inside of contains matcher, that will fail if the elements are not in the same order and will fail too if it contains more elements or is missing one.
@rvazquezglez so the purpose of the code is to examine the exact equality(same lengths, values and order) in two lists, right?

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now