Reputation: 14678
Let's say I have a List<A>
where
class A {
private Integer val;
private String name;
}
and in my test case I get this list, with uncertain size, and content. What I wish to do is to compare val
field of two list elements that I know has to be in the list with given name
fields;
List<A> list = logic.getList();
assertThat(list, allOf(hasItems(hasProperty("name", equalTo("first")),
hasItems(hasProperty("val", equalTo(***value from another member with name = "second"))));
How can I achieve this, or is this even possible with Hamcrest Matchers?
Upvotes: 3
Views: 2298
Reputation: 7679
Had to do this.
Use:
Make sure , class A overrides object equals method, comparing val and name.
class A {
private Integer val;
private String name;
}
Upvotes: 0
Reputation: 8311
You can implement custom Matcher
for your needs, e.g. to check that some items with names has same values fields:
final class FooTest {
static final class Foo {
final int val;
final String name;
// all args constructor
}
// custom matcher
static final class FoosHasSameValues extends TypeSafeMatcher<List<Foo>> {
private final Set<String> names;
// all args constructor
FoosHasSameValues(final String... names) {
this(new HashSet<>(Arrays.asList(names)));
}
@Override
protected boolean matchesSafely(final List<Foo> items) {
final List<Integer> values = items.stream()
// filter only items with specified names
.filter(i -> this.names.contains(i.name))
// select only values
.map(i -> i.val)
.collect(Collectors.toList());
if (values.size() != this.names.size()) {
// matching failed if list doesn't contains all
// needed items with names
return false;
}
// check https://stackoverflow.com/a/29288616/1723695
return values.stream().distinct().limit(2).count() <= 1;
}
@Override
public void describeTo(final Description description) {
description.appendText("has items [")
.appendValue(String.join(", ", this.names))
.appendText("] with same values");
}
}
@Test
void testMatchers() throws Exception {
MatcherAssert.assertThat(
Arrays.asList(
new Foo("first", 1),
new Foo("second", 1),
new Foo("third", 2)
),
new FoosHasSameValues("first", "second")
);
}
}
Upvotes: 2
Reputation: 2338
Writing a custom matcher cleans up your test logic:
public class AMatcher extends TypeSafeMatcher<A> {
A actual;
public AMatcher(A actual) { this.actual = actual; }
protected boolean matchesSafely(A a) {
return a.equals(actual); // or compare individual fields...
}
public void describeTo(Description d) {
d.appendText("should match "+actual); // printed out when a match isn't found.
}
}
then, to use it:
assertThat(list, allOf(new AMatcher(a1), new AMatcher(a2)));
or, if you don't want to create instances of A to create a matcher, create an AMatcher constructor that takes your 'name' and 'val' arguments.
Upvotes: 0