JavaHamcrest
JavaHamcrest copied to clipboard
Make hamcrest hasProperty to match on nested properties
Use case below
http://stackoverflow.com/questions/36363372/is-there-a-way-to-do-deep-comparison-on-a-nested-property-with-hamcrest/36364718#36364718
I use hamcrest for most of my testing ,but have encountered a issue with it not being able to test a property one level down in the object graph .A snipped of my test case is below
final List<Foo> foos= fooRepository.findAll(spec);
assertThat(results, is(notNullValue()));
assertThat(results, hasItem(hasProperty("id.fooID1", equalTo("FOOID1"))));
so here I want to check if in the list of foos I have a property id.fooID1 equla to FOOID1 .Here I am going one level down to check my nested property .This doesnt currently work in hamcrest and I get the following error.
java.lang.AssertionError:
Expected: a collection containing hasProperty("id.fooID1", "FOOID1")
but: No property "id.fooID1"
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
THink hamcrest should enhance for convenient nested property comparisons.
I use the following method to make assertions on graphs of properties.
private static <T> Matcher<T> hasGraph(String graphPath, Matcher<T> matcher) {
List<String> properties = Arrays.asList(graphPath.split("\\."));
ListIterator<String> iterator =
properties.listIterator(properties.size());
Matcher<T> ret = matcher;
while (iterator.hasPrevious()) {
ret = hasProperty(iterator.previous(), ret);
}
return ret;
}
It simply creates nested matchers but it is easy to read as it can be used with a syntax like this:
assertThat(bean, hasGraph("beanProperty.subProperty.subSubProperty", notNullValue()));
It would be nice to have this functionality on the org.hamcrest.Matchers class.
+1, unfortunately, seems like there isn't much interest for this feature :(
Not sure what to do, to support this request. I use the workaround of cristcost in some projects but would love to see that in Matchers.
Hm, great issue. My usecase - matching arbitrary properties in Spring Integration message payloads.
Good point
One small correction, you don't need to do the null value check. The matcher should handle that.
I've pushed HasPropertyWithValue.hasPropertyAtPath() based on your solution. See what you think.
I've pushed HasPropertyWithValue.hasPropertyAtPath() based on your solution. See what you think.
Due to the signature of this method, it can currently only be used if every bean on the path is of the same type T
. Similar to
public static <T> Matcher<T> hasProperty(String propertyName, Matcher<?> valueMatcher)
it should rather look like this (or use the iterator, if you insist):
@SuppressWarnings("unchecked")
public static <T> Matcher<T> hasPropertyPath(String path, Matcher<?> valueMatcher) {
Matcher<?> ret = valueMatcher;
String[] properties = path.split("\\.");
for (int i = properties.length - 1; i >= 0; --i) {
ret = new HasPropertyWithValue<>(properties[i], ret, "%s.");
}
return (Matcher<T>) ret;
}
This change would allow this:
A a = ...
Matcher<D> dMatcher = ...
assertThat(a, hasPropertyPath("b.c.d", dMatcher));
I agree with @anma, the matcher should have exactly the same signature as hasProperty
.
What's the reason for hasPropertyAtPath
to don't use Matcher<?>?
Any possibility to change the signature @sf105 ?