如何在Java中匹配对象数组的Map

时间:2015-04-13 19:30:13

标签: java mockito

如何匹配Mockito中的Map Object[]?我用了

  String[] entity= {"entity1"}
  Map<String,Object[]> queryParam = new HashMap<String,Object[]>();
  queryParam.put("entityString",entity);

  when(DB.get("SomeString", queryParam)).thenReturn(mockResponse);

但它不匹配。我觉得在实际通话中无法匹配Object[]String []。请帮帮我。

2 个答案:

答案 0 :(得分:1)

澄清答案(匹配地图)

现在很清楚,您尝试做的是匹配一个Map<String, Object[]>而不是嘲笑一个,这可能很棘手:甚至虽然Map支持equals,但数组默认情况下通过标识而非深度相等进行比较。在这种情况下,我会使用一些Hamcrest matchers

private Matcher<Map<String, Object[]>> hasParamArray(
    String key, Object... vals) {
  return hasEntry(
      equalTo(key),
      arrayContaining(vals));
}

// elsewhere
when(DB.get(
        eq("someString"),
        argThat(hasParamArray("entityString", "entity1"))))
    .thenReturn(mockResponse);

作为替代方案,请写下答案:

when(DB.get(eq("someString"), anyMap())).thenAnswer(new Answer<Response>() {
  @Override public void answer(InvocationOnMock invocation) {
    Map<String, Object[]> map =
        (Map<String, Object[]>) invocation.getArguments()[1];
    if (/* ... */) {
      return mockResponse1;
    } else if (/* ... */) {
      return mockResponse2;
    } else {
      return defaultResponse;
    }
  }
});

原始答案(嘲笑地图)

请注意Object[]String[]不兼容类型:

Map<String, Object[]> yourMap = new HashMap<>();
String[] yourStringArray = new String[] { "hello" };

// this is a compile error, or at least it should be...
yourMap.put("foo", yourStringArray);

// ...because this would otherwise be a runtime error, which is probably
// what you're experiencing right now.
Object[] yourObjectArray = yourMap.get("foo"); // returns yourStringArray
yourObjectArray[0] = new Object();

return yourStringArray[0].length();  // oops! this is that new Object()!

您可以将entity切换为Object[],然后完成:

// in your class with a call to MockitoAnnotations.initMocks(this) in setUp()
@Mock Map<String, Object[]> queryParam;

when(queryParam.get("someString")).thenReturn(new Object[] { "entity1" });

那就是说,我会建议按照大卫在评论中所说的那样做,而是使用真实的地图。经验法则是不要模拟数据对象,有三个很好的理由不模仿Map<String, Object[]>

  1. 模拟永远不会像真实的一样好。接口可以改变,就像它们支持Java 8中的流一样,真正的实现将适应这些更改,而模拟不会。在模拟真实对象时尤其如此,其中添加final修饰符(例如)会破坏您的模拟而不会实际影响您的被测系统。

  2. 与数据库不同,Map很容易手动创建。许多数据库系统具有许多依赖性,并且需要大量内存。地图没有其他依赖关系,并且是非常轻量级且经过良好测试的Java核心组件。

    (即使它是一个完整的数据库,内存中&#34;假的&#34;或其他一些独立的实现比任何合理的Mockito模拟更强大。因为JDK为您提供了许多合理的实施方案,这是一个更容易的选择。)

  3. 要适当地模拟Map需要等量的工作或更多。您的测试可能会get某些值超出地图,每个值都需要与when对齐;一个可变的Map可以简单地接受put的那些。如果地图被您的系统变异,您需要预测呼叫并更新模拟,其中真实地图将自动具有正确的行为。

    这更不用说调用containsKey(K)containsValue(V)remove(Object)size()以及您需要的地图上的许多其他方法替换以进行强大的测试。使用模拟,最简单和最合理的代码更改将打破您的测试,除非您以极大的时间和可读性为代价模拟所有内容

  4. 简而言之,这里的地图比任何Mockito模拟都要好得多。

答案 1 :(得分:0)

经过一整天的挣扎,我想出了一个解决方案。正如@Jeff Bowman所指出的,Mockito在实际通话中无法匹配Object to String。我按照this链接编写了一个自定义匹配器,可以比较我们指定的实际参数,如果匹配返回true,则Mockito会调用该调用,否则为false。这就是解决方案

      private class Query4ArgumentMatcher extends ArgumentMatcher<Map<String, Object[]>> {
     private String value;


    public Query4ArgumentMatcher(String value) {

        this.value = value;

    }

    public boolean matches(Object o) {

        if (o instanceof Map) {

            Map<String, Object[]> map = (Map<String, Object[]>) o;


            for (Map.Entry<String, Object[]> m : map.entrySet()) {

                String s = (m.getValue())[0].toString();

                if (value.contentEquals(s)) {

                    return true;

                }

            }

        }

        return false;

    }

}

并在单元测试中

 when(DB.get(eq("SomeString"), Mockito.argThat(new Query4ArgumentMatcher("entity1"))));

希望这会对某人有所帮助。