Search code examples
javajunitassertmatcherhamcrest

How to compare members of a list of complex objects with Hamcrest?


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?


Solution

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