Is there a way to do deep comparison on a nested property with Hamcrest
Solution 1
You can nest hasProperty
calls:
assertThat(results, hasItem(hasProperty("id", hasProperty("fooID1", equalTo("FOOID1")))));
For deeper nestings this might be a bit unwieldy.
Solution 2
I've achieved the result you expected with this simple utility method:
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;
}
which I am able to use in asserts like this:
assertThat(bean, hasGraph("beanProperty.subProperty.subSubProperty", notNullValue()));
check if this is of any help
Solution 3
I did not find a API solution to your problem, but found on source of 1.3 hamcrest that the HasPropertyWithValue matcher really does not dive into nested properties.
I've made a lousy solution (please observe that the messages when not found are not working properly):
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeDiagnosingMatcher;
import org.hamcrest.beans.PropertyUtil;
public class NestedPropertyMatcher<T> extends TypeSafeDiagnosingMatcher<T>{
private final String[] props;
private final String path;
private final Matcher<?> valueMatcher;
@Override
public boolean matchesSafely(T bean, Description mismatch) {
if (props.length == 1) {
return org.hamcrest.beans.HasPropertyWithValue.hasProperty(props[props.length - 1], valueMatcher).matches(bean);
} else {
Object aux = bean;
for (int i = 0; i < props.length - 1; i++) {
if (!org.hamcrest.beans.HasProperty.hasProperty(props[i]).matches(aux)) {
return false;
} else {
PropertyDescriptor pd = PropertyUtil.getPropertyDescriptor(props[i], aux);
try {
aux = pd.getReadMethod().invoke(aux);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
mismatch.appendText("Exception while trying to access property value: " + e.getLocalizedMessage());
return false;
}
}
}
return org.hamcrest.beans.HasPropertyWithValue.hasProperty(props[props.length - 1], valueMatcher).matches(aux);
}
}
private NestedPropertyMatcher(String path, String[] propertiesTokens, Matcher<?> valueMatcher) {
this.path = path;
this.props = propertiesTokens;
this.valueMatcher = valueMatcher;
}
public static <T> Matcher<T> hasPathProperty(String propertyPath, Matcher<?> valueMatcher) {
String[] props = propertyPath.split("\\.");
return new NestedPropertyMatcher<T>(propertyPath, props, valueMatcher);
}
@Override
public void describeTo(Description description) {
description.appendText("hasProperty(").appendValue(path).appendText(", ").appendDescriptionOf(valueMatcher).appendText(") did not found property");
}
}
Pretty sure that the hamcrest folks will make a better job than mine, but I think this code will be enough for you.
Gaurav Rawat
searching for answers to unanswered questions :) Human,Coder,Poet and Photographer in that order
Updated on July 31, 2022Comments
-
Gaurav Rawat almost 2 years
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)
any help or workaround on this issue .
-
Paulo Araújo about 8 yearsChanged the matcher function name to avoid collisions.
-
Gaurav Rawat about 8 yearsThanks that works like a charm but true unwieldy when it comes to a deeper hierarchy ..
-
Loathian over 6 yearsThis is great, just what I needed, thanks for sharing.