6
when(validator.isValid(Sets.newHashSet("valid"))).thenReturn(true);

When validator.isValid(set) is invoked it returns false. It is because the validator implementation creates a new set that is different that the passed one (reference is different) - the items in both sets are same.

I need to return true if sets contains same items regardless of sets instances.

Something similar to org.mockito.Matchers:

public static <T> Set<T> anySetOf(Class<T> clazz) {
        return (Set) reportMatcher(Any.ANY).returnSet();
}

except that I would pass instances not Class<T>.class.

And same for verify:

verify(validator, times(1)).isValid(Sets.newHashSet("valid"));

Is there such a matcher?

Felix
  • 3,999
  • 3
  • 42
  • 66
  • 3
    That's incorrect. If the method returns true and not false, it just means that the actual set is not equal (as defined by Set.equals()) to `Sets.newHashSet("valid")`. It has more or fewer or different elements inside. So you don't need any matcher. No matcher, or the eq() matcher would do fine if the set actually was equal to `Sets.newHashSet("valid")`. – JB Nizet Jun 30 '15 at 06:46

2 Answers2

6

Apparently JB Nizet is right. There is no need for a specific matcher.

I have tried to use my own matcher and then after removal it worked also:

public static class SetItemMatcher extends ArgumentMatcher<Set<String>> {
    public static Set<String> setOf(Set<String> items) {
        return argThat(new SetItemMatcher(items));
    }

    private final Set<String> expected;

    public SetItemMatcher(Set<String> expected) {
        this.expected = expected;
    }

    @Override
    public boolean matches(Object argument) {
        Set<?> actual = (Set<?>) argument;
        return actual.size() == expected.size()  && containsAllItems(actual);
    }

    private boolean containsAllItems(Set<?> actual) {
        for (Object o : actual) {
            if (!expected.contains(o)) {
                return false;
            }
        }
        return true;
    }
}
Felix
  • 3,999
  • 3
  • 42
  • 66
2

While Felix's answer is absolutely correct, I slightly modified it to cover more use-cases (i.e. covering any Collection with builder methods for Set and List):

  1. Work with any collection, not just sets.
  2. Added utility methods to create a list or set based on either just one item or another list/set
public class CollectionItemMatcher<T extends Collection> implements ArgumentMatcher<T> {
    public static Set setOf(Object oneItem) {
        return setOf(Sets.newHashSet(oneItem));
    }

    public static Set setOf(Collection items) {
        return ArgumentMatchers.argThat(new CollectionItemMatcher<Set>(Sets.newHashSet(items)));
    }

    public static Set emptySet() {
        return setOf(Sets.newHashSet());
    }

    public static List listOf(Object oneItem) {
        return listOf(Lists.newArrayList(oneItem));
    }

    public static List listOf(Collection items) {
        return ArgumentMatchers.argThat(new CollectionItemMatcher<List>(Lists.newArrayList(items)));
    }

    public static List emptyList() {
        return listOf(Lists.newArrayList());
    }

    private final T expected;

    public CollectionItemMatcher(T expected) {
        this.expected = expected;
    }

    @Override
    public boolean matches(T actual) {
        return actual.size() == expected.size() && containsAllItems(actual);
    }

    private boolean containsAllItems(T actual) {
        for (Object o : actual) {
            if (!expected.contains(o)) {
                return false;
            }
        }
        return true;
    }
}

Then you can do something like this:

verify(validator, times(1)).isValid(CollectionItemMatcher.setOf("valid"));
Abbas
  • 3,228
  • 1
  • 23
  • 27