使用 Mockito,我如何匹配地图的键值对?

新手上路,请多包涵

我需要根据特定键值从模拟对象发送特定值。

从具体类:

 map.put("xpath", "PRICE");
search(map);

从测试用例:

 IOurXMLDocument mock = mock(IOurXMLDocument.class);
when(mock.search(.....need help here).thenReturn("$100.00");

如何模拟此键值对的此方法调用?

原文由 Sean 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 446
1 个回答

我发现这试图解决创建带有 Map 参数的 Mockito 存根的类似问题。我不想为有问题的地图编写自定义匹配器,然后我找到了一个更优雅的解决方案:将 hamcrest-library 中的附加匹配器与 mockito 的 argThat 一起使用:

 when(mock.search(argThat(hasEntry("xpath", "PRICE"))).thenReturn("$100.00");

如果您需要检查多个条目,那么您可以使用其他 hamcrest 好东西:

 when(mock.search(argThat(allOf(hasEntry("xpath", "PRICE"), hasEntry("otherKey", "otherValue")))).thenReturn("$100.00");

对于非平凡的地图,这开始变得很长,所以我最终提取了方法来收集条目匹配器并将它们卡在我们的 TestUtils 中:

 import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.hasEntry;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.hamcrest.Matcher;
---------------------------------
public static <K, V> Matcher<Map<K, V>> matchesEntriesIn(Map<K, V> map) {
    return allOf(buildMatcherArray(map));
}

public static <K, V> Matcher<Map<K, V>> matchesAnyEntryIn(Map<K, V> map) {
    return anyOf(buildMatcherArray(map));
}

@SuppressWarnings("unchecked")
private static <K, V> Matcher<Map<? extends K, ? extends V>>[] buildMatcherArray(Map<K, V> map) {
    List<Matcher<Map<? extends K, ? extends V>>> entries = new ArrayList<Matcher<Map<? extends K, ? extends V>>>();
    for (K key : map.keySet()) {
        entries.add(hasEntry(key, map.get(key)));
    }
    return entries.toArray(new Matcher[entries.size()]);
}

所以我剩下:

 when(mock.search(argThat(matchesEntriesIn(map))).thenReturn("$100.00");
when(mock.search(argThat(matchesAnyEntryIn(map))).thenReturn("$100.00");

有一些与泛型相关的丑陋之处,我正在抑制一个警告,但至少它是干的并且隐藏在 TestUtil 中。

最后一点,注意 JUnit 4.10 中的嵌入式 hamcrest 问题。对于 Maven,我建议先导入 hamcrest-library,然后再导入 JUnit 4.11(现在是 4.12),并从 JUnit 中排除 hamcrest-core 只是为了更好的衡量:

 <dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>hamcrest-library</artifactId>
    <version>1.3</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <version>1.9.5</version>
    <scope>test</scope>
</dependency>


编辑:2017 年 9 月 1 日——根据一些评论,我更新了我的答案以显示我的 Mockito 依赖项、我在测试实用程序中的导入以及截至今天运行绿色的 junit:

 import static blah.tool.testutil.TestUtil.matchesAnyEntryIn;
import static blah.tool.testutil.TestUtil.matchesEntriesIn;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

public class TestUtilTest {

    @Test
    public void test() {
        Map<Integer, String> expected = new HashMap<Integer, String>();
        expected.put(1, "One");
        expected.put(3, "Three");

        Map<Integer, String> actual = new HashMap<Integer, String>();
        actual.put(1, "One");
        actual.put(2, "Two");

        assertThat(actual, matchesAnyEntryIn(expected));

        expected.remove(3);
        expected.put(2, "Two");
        assertThat(actual, matchesEntriesIn(expected));
    }

    @Test
    public void mockitoTest() {
        SystemUnderTest sut = mock(SystemUnderTest.class);
        Map<Integer, String> expected = new HashMap<Integer, String>();
        expected.put(1, "One");
        expected.put(3, "Three");

        Map<Integer, String> actual = new HashMap<Integer, String>();
        actual.put(1, "One");

        when(sut.search(argThat(matchesAnyEntryIn(expected)))).thenReturn("Response");
        assertThat(sut.search(actual), is("Response"));
    }

    protected class SystemUnderTest {
        // We don't really care what this does
        public String search(Map<Integer, String> map) {
            if (map == null) return null;
            return map.get(0);
        }
    }
}

原文由 Marquee 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题