如何在 Java 中匹配 Map of objects 数组
How to match a Map of objects array in Java
如何在 Mockito 中匹配 Map
of 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 []
。请帮助我。
明确答案(匹配地图)
现在很清楚,你要做的是匹配一个Map<String, Object[]>
而不是mocking一个,这可能很棘手:即使 Map
支持 equals
,arrays 默认情况下按标识而不是深度相等进行比较。在这种情况下,我会使用一些 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" });
也就是说,我建议按照 David 在评论中所说的那样做,并改用真实地图。经验法则是不要模拟数据对象,不模拟数据对象有三个很好的理由Map<String, Object[]>
:
仿制品永远不如真货。接口可以改变,就像它们在 Java 8 中支持流一样,真正的实现将适应这些变化,而模拟不会。在模拟真实对象时尤其如此,其中添加 final
修饰符(例如)会破坏您的模拟,而不会实际影响您的被测系统。
与数据库不同,Map 很容易手动创建。许多数据库系统有很多依赖关系,需要大量内存。地图没有额外的依赖关系,并且是Java.
的非常轻量级和经过良好测试的核心组件
(即使它是一个完整的数据库,内存中的 "fake" 或其他一些独立的实现也会 比任何合理的 Mockito 模拟更强大 。因为JDK为你提供了很多合理的实现方式,选择起来更容易。)
要适当地模拟一个 Map 需要等量的工作,或者更多。您的测试可能 get
地图中的某些值,每个值都需要用 when
存根;一个可变的 Map 可以简单地接受带有 put
的那些。如果 Map 被您的系统改变,您需要预测调用并更新模拟,真实的 Map 将自动具有正确的行为。
更不用说调用 containsKey(K)
、containsValue(V)
、remove(Object)
、size()
以及 Map 上您需要的许多其他方法更换以进行稳健的测试。使用模拟,最简单和最合理的代码更改将破坏您的测试,除非您以大量时间和可读性为代价模拟一切。
简而言之,在这里 Map 是比任何 Mockito mock 更好的选择。
折腾了一整天,想出了解决办法。正如@Jeff Bowman 所指定的,Mockito 在实际调用中无法将对象与字符串匹配。我遵循 this link 并编写了一个自定义匹配器,可以将实际参数与我们指定的参数进行比较,如果匹配 returns 为真,则 Mockito 模拟调用,否则为假。这就是解决方案
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"))));
希望这会对某人有所帮助。
如何在 Mockito 中匹配 Map
of 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 []
。请帮助我。
明确答案(匹配地图)
现在很清楚,你要做的是匹配一个Map<String, Object[]>
而不是mocking一个,这可能很棘手:即使 Map
支持 equals
,arrays 默认情况下按标识而不是深度相等进行比较。在这种情况下,我会使用一些 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" });
也就是说,我建议按照 David 在评论中所说的那样做,并改用真实地图。经验法则是不要模拟数据对象,不模拟数据对象有三个很好的理由Map<String, Object[]>
:
仿制品永远不如真货。接口可以改变,就像它们在 Java 8 中支持流一样,真正的实现将适应这些变化,而模拟不会。在模拟真实对象时尤其如此,其中添加
final
修饰符(例如)会破坏您的模拟,而不会实际影响您的被测系统。与数据库不同,Map 很容易手动创建。许多数据库系统有很多依赖关系,需要大量内存。地图没有额外的依赖关系,并且是Java.
的非常轻量级和经过良好测试的核心组件(即使它是一个完整的数据库,内存中的 "fake" 或其他一些独立的实现也会 比任何合理的 Mockito 模拟更强大 。因为JDK为你提供了很多合理的实现方式,选择起来更容易。)
要适当地模拟一个 Map 需要等量的工作,或者更多。您的测试可能
get
地图中的某些值,每个值都需要用when
存根;一个可变的 Map 可以简单地接受带有put
的那些。如果 Map 被您的系统改变,您需要预测调用并更新模拟,真实的 Map 将自动具有正确的行为。更不用说调用
containsKey(K)
、containsValue(V)
、remove(Object)
、size()
以及 Map 上您需要的许多其他方法更换以进行稳健的测试。使用模拟,最简单和最合理的代码更改将破坏您的测试,除非您以大量时间和可读性为代价模拟一切。
简而言之,在这里 Map 是比任何 Mockito mock 更好的选择。
折腾了一整天,想出了解决办法。正如@Jeff Bowman 所指定的,Mockito 在实际调用中无法将对象与字符串匹配。我遵循 this link 并编写了一个自定义匹配器,可以将实际参数与我们指定的参数进行比较,如果匹配 returns 为真,则 Mockito 模拟调用,否则为假。这就是解决方案
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"))));
希望这会对某人有所帮助。