Hamcrest matcher for checking return value of method in collection
Asked Answered
C

3

27

hasProperty can be used with hasItem to check for the value of a given property, eg:

Matcher hasName = Matchers.<Person>hasProperty("name", is("Winkleburger"));
assertThat(names, hasItem(hasName));

This is fine when name is a property, ie: there is a method called getName().

Is there a matcher that will check a method that isn't a property? ie: in this case, it will check the return value of the method name() rather than getName(), for items in the collection.

Crescendo answered 3/9, 2014 at 13:56 Comment(1)
so, you want to call the name() method on all items of a collection?Regional
N
30

You can use another Hamcrest built-in for this, a FeatureMatcher. These are designed to be combined with other matchers after they transform your input into something else. So in your case you would do something like this:

@Test
public void test1() {
    List<Person> names = new ArrayList<>();
    names.add(new Person("Bob"));
    names.add(new Person("i"));

    assertThat(names, hasItem(name(equalTo("Winkleburger"))));
}

private FeatureMatcher<Person, String> name(Matcher<String> matcher) {
    return new FeatureMatcher<Person, String>(matcher, "name", "name") {
        @Override
        protected String featureValueOf(Person actual) {
            return actual.name();
        }
    };
}

The benefit you'll get with this over a custom matcher is that it's fully reusable and composable with other matchers as all it does is the data-extraction then defers to whatever other matcher you want. You're also going to get appropriate diagnostics, e.g. in the above example you will if you change the assert to a value that's not present you will receive:

java.lang.AssertionError: 
    Expected: a collection containing name "Batman"
    but: name was "Bob", name was "Winkleburger"
Neurocoele answered 3/9, 2014 at 21:5 Comment(0)
H
2

You can write one yourself:

public class HasName extends TypeSafeMatcher<MyClass> {
    private String expectedName;

    private HasName(String expectedName) {
        this.expectedName = expectedName;
    }

    @Override
    public boolean matchesSafely(MyClass obj) {
        return expectedName.equals(obj.name());
    }

    @Factory
    public static Matcher<MyClass> hasName(String expectedName) {
        return new HasName(expectedName);
    }
}

Where MyClass is a class or interface which defines the name() method.

Humic answered 3/9, 2014 at 20:30 Comment(0)
H
0

Here's a generic primitive for specifying a property as a method reference:

static <T, U> Matcher<T> hasProperty( Function<T, U> getter, 
                                      String name,
                                      Matcher<? super U> subMatcher ) {

  return new FeatureMatcher<T, U>( subMatcher, name, name ) {
    @Override
    protected U featureValueOf( T actual ) {
      return getter.apply( actual );
    }
  };
}

Which you could use as:

assertThat(person, hasProperty(Person::getName, "name", is("Winkleburger")));
Hindoo answered 10/4 at 11:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.