如何在Mockito中匹配Object[]
的Map
?我使用
String[] entity= {"entity1"}
Map<String,Object[]> queryParam = new HashMap<String,Object[]>();
queryParam.put("entityString",entity);
和
when(DB.get("SomeString", queryParam)).thenReturn(mockResponse);
但它并不匹配。我觉得在实际通话中无法匹配Object[]
和String []
。请帮帮我。
明确的答案(匹配地图)
现在很明显,您要做的是匹配一个Map<String, Object[]>
,而不是嘲笑,这可能很棘手:尽管Map
支持equals
,但数组默认情况下是按身份进行比较,而不是按深度相等进行比较。在这种情况下,我会使用一些Hamcrest匹配器:
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[]>
:
模拟永远不会像真实的那样好。接口可能会发生变化,就像它们在Java8中支持流一样,而实际的实现将适应这些变化,而mock则不会。当模拟真实对象时尤其如此,例如,添加
final
修饰符会破坏模拟,而不会实际影响测试中的系统。与数据库不同,Map非常容易手动创建。许多数据库系统具有许多依赖关系,并且需要大量内存。映射没有额外的依赖关系,是非常轻量级且经过良好测试的Java核心组件。
(即使它是一个完整的数据库,内存中的"fake"或其他独立实现也会比任何合理的Mockito mock更健壮。因为JDK为您提供了许多合理的实现,所以这是一个更容易的选择。)
要适当地模拟Map,需要相当多的工作。您的测试可能会将某些值
get
排除在映射之外,每个值都需要用when
进行存根处理;可变映射可以简单地接受那些具有CCD_ 16的映射。如果Map被您的系统更改,您需要预测调用并更新mock,在mock中,真正的Map将自动具有正确的行为。更不用说调用
containsKey(K)
、containsValue(V)
、remove(Object)
、size()
以及Map上的许多其他方法了,您需要替换这些方法才能进行健壮的测试。使用mock,最简单、最合理的代码更改将破坏您的测试,除非您以牺牲时间和可读性为代价模拟所有。
简而言之,Map在这里比任何Mockito mock都是更好的选择。
经过一整天的努力,我找到了一个解决方案。正如@Jeff Bowman所指定的,Mockito在实际调用中无法将Object与String匹配。我遵循了这个链接,编写了一个自定义匹配器,可以将真实的参数与我们指定的参数进行比较,如果匹配返回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"))));
希望这能帮助到别人。