Is there a Hamcrest "for each" Matcher that asserts all elements of a Collection or Iterable match a single specific Matcher?

28,306

Solution 1

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;
    }
}

Solution 2

IMHO this is much more readable:

people.forEach(person -> Assert.assertThat(person.getGender()), Is.is("male"));
Share:
28,306
E-Riz
Author by

E-Riz

SOreadytohelp

Updated on July 05, 2022

Comments

  • E-Riz
    E-Riz over 1 year

    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
    E-Riz over 8 years
    It seems that generics is causing problems. I'm getting a compilation problem because assertThat() expects argument types T, Matcher<?super T> but it's getting Iterable<Person>, Matcher<Iterable<Object>>
  • Sotirios Delimanolis
    Sotirios Delimanolis over 8 years
    @E-Riz I've updated with a full example. If yours is different, please edit your question to include it.
  • E-Riz
    E-Riz over 8 years
    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
    E-Riz over 8 years
    To get it to compile for Java 7, I had to declare the hasProperty Matcher like this: HasPropertyWithValue.<Person>hasProperty("gender", is("Male")) which pretty much destroys the readability improvements that Hamcrest is supposed to provide :-(
  • GhostCat
    GhostCat over 6 years
    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 ...
  • Petar Mitrovic
    Petar Mitrovic almost 4 years
    This is anti-pattern. Asserts should not be placed inside a loop.
  • Raghu
    Raghu about 3 years
    @PetarMitrovic Why Asserts should not be placed inside loop?
  • Petar Mitrovic
    Petar Mitrovic about 3 years
    Simply put you won't be able to get the whole picture about the failing asserts as the test runner would exit as soon as it encounters the first failing one.
  • andresp
    andresp almost 2 years
    but that is not Hamcrest