Reputation: 8807
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.
Upvotes: 27
Views: 5820
Reputation: 140
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")));
Upvotes: 0
Reputation: 21184
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"
Upvotes: 30
Reputation: 1546
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.
Upvotes: 2