2

我依赖于一个将 Map 作为参数的方法。

public interface Service {
    void doSomething(Map<String, String> map);
}

我想写一个断言,这个依赖是用适当的地图内容调用的。像这样的东西:

@RunWith(JMock.class)
public class MainTest {
    private Mockery context = new Mockery();
    private Service service = context.mock(Service.class);
    private Main main = new Main(service);

    @Test
    public void test() {
        context.checking(new Expectations(){{
            oneOf(service).doSomething(with(hasEntry("test", "test")));
        }});
        main.run();
    }
}

不幸的是,这无法编译,因为 hasEntry 在映射通用参数中有通配符:

public static <K, V> org.hamcrest.Matcher<java.util.Map<? extends K, ? extends V>> hasEntry(K key, V value);

有没有办法为地图内容编写 JMock 期望?

4

2 回答 2

3

由于我们达到了 Java 泛型的限制,因此没有一个好的答案。jMock 所需的泛型与 assertThat() 所需的泛型之间存在矛盾

我倾向于添加一个具有表现力名称的辅助方法来强制类型。

@Test public void test() {
  context.checking(new Expectations(){{
    oneOf(service).doSomething(with(mapIncluding("test", "test")));
  }});

  main.run();
}

@SuppressWarnings({"unchecked", "rawtypes"})
private Matcher<Map<String, String>> mapIncluding(String key, String value) {
   return (Matcher)Matchers.hasEntry(key, value);
};

是的,这很丑陋。我只能道歉,这是我们似乎能做的最好的事情。也就是说,我很少需要关闭类型,我可以给它一个在域中有意义的名称,并且我已经将取消选中本地化为辅助方法。

于 2012-05-28T09:31:51.747 回答
0

我最终创建了一个方法指定(),允许将通用匹配器向下转换为更具体的匹配器

public static <T> Matcher<T> specify(final Matcher<? super T> matcher) {
    return new TypeSafeMatcher<T>() {
        @Override
        protected boolean matchesSafely(T item) {
            return matcher.matches(item);
        }

        @Override
        public void describeTo(Description description) {
            matcher.describeTo(description);
        }
    };
}

使用这种方法,我可以向下转换任何现有的通用匹配器,例如 hasEntry()

public <K, V> Matcher<Map<? extends K, ? extends V>> hasEntry(K key, V value)

以通用安全的方式转换为更具体的,如下所示:

private static <K,V> Matcher<Map<K, V>> aMapHavingEntry(K key, V value) {
    return specify(hasEntry(key, value));
}

现在我可以使用这个特定的匹配器作为期望参数:

    context.checking(new Expectations() {{
        oneOf(service).doSomething(with(aMapHavingEntry("test", "test")));
    }});

使用指定()方法,我为最流行的接口创建了一堆特定的匹配器:Map、Collection、List、Set,例如:

private static <K,V> Matcher<Map<K, V>> aMapHavingEntry(K key, V value) {
    return specify(hasEntry(key, value));
}

private static <K> Matcher<Collection<K>> aCollectionContainingInAnyOrder(K... items) {
    return specify(containsInAnyOrder(items));
}

我还建议向 JMock 添加相同的功能,尽管我得到的只是沉默。

于 2012-06-16T12:30:32.120 回答