有没有办法与Hamcrest对嵌套属性进行深度比较

时间:2016-04-01 18:26:30

标签: java unit-testing hamcrest

我在大多数测试中都使用了hamcrest,但遇到了一个问题,它无法在对象图中测试一个级别的属性。我的测试用例的剪辑在下面

final List<Foo> foos= fooRepository.findAll(spec);
      assertThat(results, is(notNullValue()));
      assertThat(results, hasItem(hasProperty("id.fooID1", equalTo("FOOID1"))));

所以在这里我要检查是否在foos列表中我有一个属性id.fooID1 equla到FOOID1。这是我向下一级检查我的嵌套属性。这目前无法在hamcrest工作,我得到以下错误。

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)

有关此问题的任何帮助或解决方法。

3 个答案:

答案 0 :(得分:18)

您可以嵌套hasProperty来电:

assertThat(results, hasItem(hasProperty("id", hasProperty("fooID1", equalTo("FOOID1")))));

对于更深的嵌套,这可能有点笨拙。

答案 1 :(得分:4)

我已经通过这个简单的实用方法获得了您期望的结果:

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;
}

我可以在这样的断言中使用:

 assertThat(bean, hasGraph("beanProperty.subProperty.subSubProperty", notNullValue()));

检查这是否有任何帮助

答案 2 :(得分:2)

我没有找到问题的API解决方案,但是在1.3 hamcrest的源代码中发现HasPropertyWithValue匹配器实际上没有深入到嵌套属性中。

我已经制作了一个糟糕的解决方案(请注意,找不到的消息不能正常工作):

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");
    }
}

非常肯定Hamcrest的人会比我的更好,但我认为这段代码对你来说已经足够了。