有没有办法用 Hamcrest 对嵌套 属性 进行深入比较
Is there a way to do deep comparison on a nested property with 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)
有关此问题的任何帮助或解决方法。
我没有找到 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 的人会比我的做得更好,但我认为这段代码对你来说已经足够了。
您可以嵌套 hasProperty
个调用:
assertThat(results, hasItem(hasProperty("id", hasProperty("fooID1", equalTo("FOOID1")))));
对于更深的嵌套,这可能有点笨拙。
我已经通过这个简单的实用方法达到了您预期的结果:
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()));
检查这是否有帮助
您可以尝试使用Hamcrest BeanMatcher APT generator。它可以为您的 Foo
bean(以及所有嵌套 bean)生成 Matcher,因此您将能够使用以下构造
assertThat(results, hasItem(fooMatcher()
.withId(idBeanMatcher()
.withFooID1("FOOID1"))));
对于这种情况,它看起来有点矫枉过正,但如果您需要检查更复杂的情况,它可能会大大简化代码。
此外,它会在每次更新时自动重新生成 BeanMatchers,因此如果您更改某些 属性 的名称(例如 FooID1
到 FooId1
) 比一些硬编码字符串更容易检测和修复。
我的大部分测试都使用 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)
有关此问题的任何帮助或解决方法。
我没有找到 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 的人会比我的做得更好,但我认为这段代码对你来说已经足够了。
您可以嵌套 hasProperty
个调用:
assertThat(results, hasItem(hasProperty("id", hasProperty("fooID1", equalTo("FOOID1")))));
对于更深的嵌套,这可能有点笨拙。
我已经通过这个简单的实用方法达到了您预期的结果:
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()));
检查这是否有帮助
您可以尝试使用Hamcrest BeanMatcher APT generator。它可以为您的 Foo
bean(以及所有嵌套 bean)生成 Matcher,因此您将能够使用以下构造
assertThat(results, hasItem(fooMatcher()
.withId(idBeanMatcher()
.withFooID1("FOOID1"))));
对于这种情况,它看起来有点矫枉过正,但如果您需要检查更复杂的情况,它可能会大大简化代码。
此外,它会在每次更新时自动重新生成 BeanMatchers,因此如果您更改某些 属性 的名称(例如 FooID1
到 FooId1
) 比一些硬编码字符串更容易检测和修复。