比较 Set 中项目的 Mockito 匹配器,而不是 Set 引用本身

Mockito matcher that compares items in Set, not the Set reference itself

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

当调用 validator.isValid(set) 时,它 returns false。这是因为验证器实现创建了一个新的集合,它与传递的集合不同(引用不同)——两个集合中的项目是相同的。

我需要 return true 如果集合包含相同的项目而不考虑集合实例。

类似于org.mockito.Matchers的内容:

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

除了我会传递实例而不是 Class<T>.class

verify也一样:

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

有这样的匹配器吗?

显然 JB Nizet 是对的。不需要特定的匹配器。

我试过使用我自己的匹配器,然后在移除后它也起作用了:

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 的回答绝对正确,但我稍微修改了一下以涵盖更多内容 use-cases:

  1. 使用任何集合,而不仅仅是集合。
  2. 添加了实用程序方法以仅基于一项或另一项创建列表或集合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 List listOf(Object oneItem) {
        return listOf(Lists.newArrayList(oneItem));
    }

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

    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;
    }
}

那么你可以这样做:

verify(validator, times(1)).isValid(CollectionItemMatcher.setOf("valid"));