我正在尝试使用hamcrest匹配器将对象列表与其属性的列表/数组进行匹配。对于一个属性值,这不是问题,因为我可以这样做:
assertThat(savedGroup.getMembers(),
containsInAnyOrder(hasProperty("name", is(NAMES[0]))));
对于多个属性值,我可以使用多个hasProperty()调用
assertThat(savedGroup.getMembers(),
containsInAnyOrder(
hasProperty("name", is(NAMES[0])),
hasProperty("name", is(NAMES[1]))));
但是有没有一种通用的方法来匹配NAMES数组中的所有值?
答案 0 :(得分:3)
执行此操作的最佳方式(IMO)是将重载的containsInAnyOrder
匹配器与自定义FeatureMatcher
结合使用。最终你的代码看起来像这样:
String[] expectedNames = new String[] { "John", "Bob", "Carol"};
assertThat(savedGroup.getMembers(), hasNames(expectedNames));
hasNames
的实施方式如下:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(name)).collect(Collectors.toList()));
}
最后一部分是对name
的调用,它生成一个Matcher,它将以类型安全的方式从对象中提取属性:
private Matcher<Member> name(String name) {
return new FeatureMatcher<Member, String>(equalTo(name), "name", "name") {
@Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}
这样做的好处是:
hasProperty
hasNames
assertThat(member, has(name("Fred")))
通过将equalTo
子匹配器移动到hasNames调用中,您可以获得更多可组合性:
private Matcher<Iterable<? extends Member>> hasNames(String[] expectedNames) {
return containsInAnyOrder(Arrays.stream(expectedNames).map(name -> name(equalTo(name))).collect(Collectors.toList()));
}
private Matcher<Member> name(Matcher<String> nameMatcher) {
return new FeatureMatcher<Member, String>(nameMatcher, "name", "name") {
@Override
protected String featureValueOf(Member actual) {
return actual.getName();
}
};
}
答案 1 :(得分:1)
containsInAnyOrder
的一个重载接受一组匹配器作为其参数。因此你可以这样做:
assertThat(
savedGroup.getMembers(),
containsInAnyOrder(
Stream.of(NAMES)
.map(name -> hasProperty("name", is(name)))
.collect(Collectors.toList())
));
(如果使用Java 8,否则需要添加一个构建集合的循环)
答案 2 :(得分:0)
需要进行一些清理(描述输出),但我认为它确实可以解决您的问题:
package org.example.matchers;
import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.TypeSafeMatcher;
public class ContainsArrayElementsInAnyOrder<T> extends TypeSafeMatcher<List<T>> {
private T[] toMatch;
public ContainsArrayElementsInAnyOrder(final T[] toMatch) {
this.toMatch = toMatch;
}
@Override
protected boolean matchesSafely(List<T> item) {
if(item.size() != toMatch.length) {
return false;
}
for (T t : toMatch) {
if(!item.contains(t)) {
return false;
}
}
return true;
}
@Override
public void describeMismatchSafely(List<T> item, Description mismatchDescription) {
mismatchDescription.appendValueList("[", ",", "]", item);
}
@Override
public void describeTo(Description description) {
description.appendValueList("[", ",", "]", toMatch);
}
@Factory
public static <T> ContainsArrayElementsInAnyOrder<T> containsArrayElementsInAnyOrder(T[] elements) {
return new ContainsArrayElementsInAnyOrder<T>(elements);
}
}
测试:
@Test
public void shouldContainsInAnyOrderSameElementsInArrayAsInList() {
final String[] NAME = new String[]{"name3", "name1", "name2"};
final List<String> result = new ArrayList<>(3);
result.add("name2");
result.add("name1");
result.add("name4");
assertThat(result, containsArrayElementsInAnyOrder(NAME));
}
如果不匹配则输出:
java.lang.AssertionError:
Expected: ["name3","name1","name2"]
but: ["name2","name1","name4"]
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at ..