How do I assert an Iterable contains elements with a certain property?
Solution 1
Thank you @Razvan who pointed me in the right direction. I was able to get it in one line and I successfully hunted down the imports for Hamcrest 1.3.
the imports:
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
the code:
assertThat( myClass.getMyItems(), contains(
hasProperty("name", is("foo")),
hasProperty("name", is("bar"))
));
Solution 2
AssertJ provides an excellent feature in extracting()
: you can pass Function
s to extract fields. It provides a check at compile time.
You could also assert the size first easily.
It would give :
import static org.assertj.core.api.Assertions;
Assertions.assertThat(myClass.getMyItems())
.hasSize(2)
.extracting(MyItem::getName)
.containsExactlyInAnyOrder("foo", "bar");
containsExactlyInAnyOrder()
asserts that the list contains only these values whatever the order.
To assert that the list contains these values whatever the order but may also contain other values use contains()
:
.contains("foo", "bar");
As a side note : to assert multiple fields from elements of a List
, with AssertJ we do that by wrapping expected values for each element into a tuple()
function :
import static org.assertj.core.api.Assertions;
import static org.assertj.core.groups.Tuple;
Assertions.assertThat(myClass.getMyItems())
.hasSize(2)
.extracting(MyItem::getName, MyItem::getOtherValue)
.containsExactlyInAnyOrder(
tuple("foo", "OtherValueFoo"),
tuple("bar", "OtherValueBar")
);
Solution 3
Its not especially Hamcrest, but I think it worth to mention here. What I use quite often in Java8 is something like:
assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName())));
(Edited to Rodrigo Manyari's slight improvement. It's a little less verbose. See comments.)
It may be a little bit harder to read, but I like the type and refactoring safety. Its also cool for testing multiple bean properties in combination. e.g. with a java-like && expression in the filter lambda.
Solution 4
Try:
assertThat(myClass.getMyItems(),
hasItem(hasProperty("YourProperty", is("YourValue"))));
Solution 5
Assertj is good at this.
import static org.assertj.core.api.Assertions.assertThat;
assertThat(myClass.getMyItems()).extracting("name").contains("foo", "bar");
Big plus for assertj compared to hamcrest is easy use of code completion.
Kevin Pauli
Updated on July 08, 2022Comments
-
Kevin Pauli almost 2 years
Assume I want to unit test a method with this signature:
List<MyItem> getMyItems();
Assume
MyItem
is a Pojo that has many properties, one of which is"name"
, accessed viagetName()
.All I care about verifying is that the
List<MyItem>
, or anyIterable
, contains twoMyItem
instances, whose"name"
properties have the values"foo"
and"bar"
. If any other properties don't match, I don't really care for the purposes of this test. If the names match, it's a successful test.I would like it to be one-liner if possible. Here is some "pseudo-syntax" of the kind of thing I would like to do.
assert(listEntriesMatchInAnyOrder(myClass.getMyItems(), property("name"), new String[]{"foo", "bar"});
Would Hamcrest be good for this type of thing? If so, what exactly would be the hamcrest version of my pseudo-syntax above?
-
Kevin Bowersox over 11 yearsI really like your solution, but should he mod all that code for a test?
-
Brad over 11 yearsI figure that every answer here will require some test setup, execution of the method to test, and then assert the properties. There's no real overhead to my answer from what I can see, only that I have two assertions on seaprate lines so that a failed assertion can clearly identify what value is missing.
-
Rodrigo Manyari almost 8 yearsSlight improvement: assertTrue(myClass.getMyItems().stream().anyMatch(item -> "foo".equals(item.getName()));
-
Hartmut Pfarr over 7 yearsjust as a side node - this is a hamcrest solution (not assertj)
-
Abdull over 7 years@RodrigoManyari, closing parenthesis missing
-
Max about 7 yearsIt would be best to also include a message within assertTrue so that the error message is more intelligible. Without a message, if it fails, JUnit will just throw a AssertionFailedError without any error message. So best to include something like "results should contain new MyItem(\"foo\")".
-
Brad about 7 yearsYes you are right. I'd recommend Hamcrest in any case, and I never use assertTrue() these days
-
Tayab Hussain over 5 yearsTo a side note your POJO or DTO should define the equals method
-
PeMa over 5 yearsDon't get why this has no upvotes. I think, this is the best answer, by far.
-
Sangimed over 5 yearsThe assertJ library is much more readable then JUnit assertion API.
-
davidxxx over 5 years@Sangimed Agreed and also I prefer it to hamcrest.
-
Giulio Caccin almost 5 yearsThis solution waste the possibility to show an appropriate error message.
-
Mario Eis almost 5 years@GiulioCaccin I don't think it does. If you use JUnit, you could/should use the overloaded assertion methods and write assertTrue(..., "My own test failure message"); See more on junit.org/junit5/docs/current/api/org/junit/jupiter/api/…
-
Giulio Caccin almost 5 yearsI mean, if you do the assertion against a Boolean, you lose the ability to print automatically the actual/expected difference. It is possible to assert using a matcher, but you need to modify this response to be similar to other in this page to do it.
-
Mario Eis almost 5 yearsHm, I can't follow you. There is no difference or comparison here. The Question was, whether the collection contains items or not. So just do assertTrue(..., "The collection doesn't contain items with the name 'foo'") and in case of a failure, your test report will contain the correct message. If your message, for any reason, has to contain the test data, just add it to the massage.
-
Terran almost 4 yearsIn my opinion this is slightly less readable as it takes apart the "actual value" from the "expected value" and puts them in an order that needs to match.
-
Gaurav over 2 yearsOne more way Frank : assertThat(list) .containsAll(Arrays.asList(id1,id2));
-
Jorge Campos about 2 yearsIf you don't know the order the items are use
containsInAnyOrder
(from same parent class) instead :)