Roberto Trunfio Roberto Trunfio - 3 months ago 27
Java Question

Testing in Hamcrest that exists only one item in a list with a specific property

With Hamcrest we can easily test that exists at least one item in a list with a specific property, e.g.

List<Pojo> myList = ....

MatcherAssert.assertThat(myList, Matchers.hasItem(Matchers.<Pojo>hasProperty("fieldName", Matchers.equalTo("A funny string")))));


where the class
Pojo
is something like:

public class Pojo{
private String fieldName;
}


That's nice, but how can I check that there is exactly one object in the list with the specificed properties?

Answer

You might have to write your own matcher for this. (I prefer the fest assertions and Mockito, but used to use Hamcrest...)

For example...

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.core.IsCollectionContaining;

public final class CustomMatchers {

    public static <T> Matcher<Iterable<? super T>> exactlyNItems(final int n, Matcher<? super T> elementMatcher) {
        return new IsCollectionContaining<T>(elementMatcher) {
            @Override
            protected boolean matchesSafely(Iterable<? super T> collection, Description mismatchDescription) {
                int count = 0;
                boolean isPastFirst = false;

                for (Object item : collection) {

                    if (elementMatcher.matches(item)) {
                        count++;
                    }
                    if (isPastFirst) {
                        mismatchDescription.appendText(", ");
                    }
                    elementMatcher.describeMismatch(item, mismatchDescription);
                    isPastFirst = true;
                }

                if (count != n) {
                    mismatchDescription.appendText(". Expected exactly " + n + " but got " + count);
                }
                return count == n;
            }
        };
    }
}

You can now do...

    List<TestClass> list = Arrays.asList(new TestClass("Hello"), new TestClass("World"), new TestClass("Hello"));

    assertThat(list, CustomMatchers.exactlyNItems(2, hasProperty("s", equalTo("Hello"))));

Example fail output when the list is...

    List<TestClass> list = Arrays.asList(new TestClass("Hello"), new TestClass("World"));

...will be...

Exception in thread "main" java.lang.AssertionError: 
Expected: a collection containing hasProperty("s", "Hello")
     but: , property 's' was "World". Expected exactly 2 but got 1

(You might want to customise this a bit)

By the way, "TestClass" is...

public static class TestClass {
    String s;

    public TestClass(String s) {
        this.s = s;
    }

    public String getS() {
        return s;
    }
}