79

Given a Collection or Iterable of items, is there any Matcher (or combination of matchers) that will assert every item matches a single Matcher?

For example, given this item type:

public interface Person {
    public String getGender();
}

I'd like to write an assertion that all items in a collection of Persons have a specific gender value. I'm thinking something like this:

Iterable<Person> people = ...;
assertThat(people, each(hasProperty("gender", "Male")));

Is there any way to do this without writing the each matcher myself?

E-Riz
  • 31,431
  • 9
  • 97
  • 134
  • 5
    Probably you can use the everyItem Matcher: http://junit.sourceforge.net/javadoc/org/junit/matchers/JUnitMatchers.html – René Winkler Mar 04 '15 at 16:54

3 Answers3

104

Use the Every matcher.

import org.hamcrest.beans.HasPropertyWithValue;
import org.hamcrest.core.Every;
import org.hamcrest.core.Is;
import org.junit.Assert;

Assert.assertThat(people, (Every.everyItem(HasPropertyWithValue.hasProperty("gender", Is.is("male")))));

Hamcrest also provides Matchers#everyItem as a shortcut to that Matcher.


Full example

@org.junit.Test
public void method() throws Exception {
    Iterable<Person> people = Arrays.asList(new Person(), new Person());
    Assert.assertThat(people, (Every.everyItem(HasPropertyWithValue.hasProperty("gender", Is.is("male")))));
}

public static class Person {
    String gender = "male";

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }
}
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • It seems that generics is causing problems. I'm getting a compilation problem because `assertThat()` expects argument types `T, Matcher` but it's getting `Iterable, Matcher>` – E-Riz Mar 04 '15 at 18:11
  • @E-Riz I've updated with a full example. If yours is different, please edit your question to include it. – Sotirios Delimanolis Mar 04 '15 at 18:53
  • 1
    You example still has the generics-related compilation problem, but only when compiled against JDK 7; with JDK 8 it compiles fine. I wasn't aware that the generics spec changed between 7 and 8 but apparently they realized the compiler was being stupid and fixed it. – E-Riz Mar 04 '15 at 19:04
  • 2
    To get it to compile for Java 7, I had to declare the hasProperty Matcher like this: `HasPropertyWithValue.hasProperty("gender", is("Male"))` which pretty much destroys the readability improvements that Hamcrest is supposed to provide :-( – E-Riz Mar 04 '15 at 19:08
  • 1
    And beyond that, woohaaa, reflection and by-string access. I find that super-ugly. I would rather manually iterate the collection and assertThat() on Person::getGender() than ... – GhostCat Aug 02 '17 at 08:53
2

IMHO this is much more readable:

people.forEach(person -> Assert.assertThat(person.getGender()), Is.is("male"));
Markus
  • 1,887
  • 18
  • 23
-2

More readable than the approved answer and no separate assertions in a loop:

import static org.assertj.core.api.Assertions.assertThat;

assertThat(people).allMatch((person) -> {
  return person.gender.equals("male");
});
xaviert
  • 5,653
  • 6
  • 29
  • 31