Is there a way to create a static method that will return a Hamcrest matcher that matches a collection or matches null?

StackOverflow https://stackoverflow.com/questions/21117868

  •  28-09-2022
  •  | 
  •  

Question

I'm trying to introduce Hamcrest matchers into some of the code for my team. In order to take the complexity out of matching a collection of instances, I want to write a helper method for each one of my matchers that I expect that it would be desirable to match collections of. So in essence I'm wrapping containsInAnyOrder. That being said, if someone passes in null as both the expected and actual, I want it to match. But the way I have my code written, it will throw a NullPointerException if null is passed in for the expected. So I want to return an IsNull matcher if null is passed in as the expected. Here's my sample code:

/**
 * Matches all Foo objects in an order agnostic manner.
 * @param expected The collection of Foo objects to be matched.
 * @return A matcher that will match a collection of Foos
 */
@SuppressWarnings("unchecked")
public static Matcher<Iterable<? extends Foo>> matchesAllfoos(Collection<Foo> expected)
{
    if (expected == null)
    {
        // Doesn't work because Matcher<Iterable> is not a Matcher<Iterable<? extends Foo>>
        return nullValue(Iterable.class);
    }

    // The cast is here to provide a hint to Java as to which overloaded method to choose.
    // See http://stackoverflow.com/questions/18614621/conflicting-overloads-for-hamcrest-matcher
    return containsInAnyOrder((Collection)Collections2.transform(expected, FOO_TO_MATCHER));
}

So how do I accomplish what I want to do? Using nullValue() doesn't work because then it expects me to return Matcher. Casting nullValue(Iterable.class) doesn't work. Any ideas?

Was it helpful?

Solution

If you look closely, nullValue(Class<T>) simply creates a IsNull<T> matcher.

So, if you are calling:

public static <T> Matcher<T> nullValue(Class<T> type) {
    return new IsNull<T>();
}

In the end (as the generics are giving a hard time because you can't figure out the runtime class of a potentially null object), you could just call the constructor directly:

new IsNull<Iterable<? extends Foo>>();

So a solution would be to just use it, without the nullValue() utility method:

/**
 * Matches all Foo objects in an order agnostic manner.
 * @param expected The collection of Foo objects to be matched.
 * @return A matcher that will match a collection of Foos
 */
@SuppressWarnings("unchecked")
public static Matcher<Iterable<? extends Foo>> matchesAllfoos(Collection<Foo> expected)
{
    if (expected == null)
    {
        return new IsNull<Iterable<? extends Foo>>();
    }

    // The cast is here to provide a hint to Java as to which overloaded method to choose.
    // See http://stackoverflow.com/questions/18614621/conflicting-overloads-for-hamcrest-matcher
    return containsInAnyOrder((Collection)Collections2.transform(expected, FOO_TO_MATCHER));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top