Search code examples
javahamcrest

Hamcrest matcher for checking return value of method in collection


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.


Solution

  • 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"