Java 使用 Mockito,如何匹配地图的键值对?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2580369/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Mockito, how do I match against the key-value pair of a map?
提问by Sean
I need to send a specific value from a mock object based on a specific key value.
我需要从基于特定键值的模拟对象发送特定值。
From the concrete class:
从具体类:
map.put("xpath", "PRICE");
search(map);
From the test case:
从测试用例:
IOurXMLDocument mock = mock(IOurXMLDocument.class);
when(mock.search(.....need help here).thenReturn("0.00");
How do I mock this method call for this key value pair?
如何模拟此键值对的此方法调用?
回答by Bozho
Doesn't this work?
这不行吗?
Map<String, String> map = new HashMap<String, String>();
map.put("xpath", "PRICE");
when(mock.search(map)).thenReturn("0.00");
The Map
parameter should behave the same way as other parameters.
该Map
参数的行为方式应与其他参数相同。
回答by denis.solonenko
Seems like what you need is an Answer
:
看起来你需要的是一个Answer
:
IOurXMLDocument doc = mock(IOurXMLDocument.class);
when(doc.search(Matchers.<Map<String,String>>any())).thenAnswer(new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
Map<String, String> map = (Map<String, String>) invocation.getArguments()[0];
String value = map.get("xpath");
if ("PRICE".equals(value)) {
return "0.00";
} else if ("PRODUCTNAME".equals(value)) {
return "Candybar";
} else {
return null;
}
}
});
But what seems like a better idea is to not use primitive Map
as parameter to your search method - you could probably transform this map into a pojo with price
and productName
attributes. Just an idea :)
但是似乎更好的主意是不要使用原始类型Map
作为搜索方法的参数 - 您可能可以将此地图转换为具有price
和productName
属性的 pojo 。只是一个想法:)
回答by Marquee
I found this trying to solve a similar issue creating a Mockito stub with a Map parameter. I didn't want to write a custom matcher for the Map in question and then I found a more elegant solution: use the additional matchers in hamcrest-librarywith mockito's argThat:
我发现这试图解决使用 Map 参数创建 Mockito 存根的类似问题。我不想为有问题的 Map 编写自定义匹配器,然后我找到了一个更优雅的解决方案:将hamcrest-library 中的其他匹配器与 mockito 的 argThat 一起使用:
when(mock.search(argThat(hasEntry("xpath", "PRICE"))).thenReturn("0.00");
If you need to check against multiple entries then you can use other hamcrest goodies:
如果您需要检查多个条目,那么您可以使用其他 hamcrest 好东西:
when(mock.search(argThat(allOf(hasEntry("xpath", "PRICE"), hasEntry("otherKey", "otherValue")))).thenReturn("0.00");
This starts to get long with non-trivial maps, so I ended up extracting methods to collect the entry matchers and stuck them in our TestUtils:
对于非平凡的地图,这开始变得很长,所以我最终提取了方法来收集条目匹配器并将它们卡在我们的 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()]);
}
So I'm left with:
所以我只剩下:
when(mock.search(argThat(matchesEntriesIn(map))).thenReturn("0.00");
when(mock.search(argThat(matchesAnyEntryIn(map))).thenReturn("0.00");
There's some ugliness associated with the generics and I'm suppressing one warning, but at least it's DRY and hidden away in the TestUtil.
有一些与泛型相关的丑陋之处,我正在取消一个警告,但至少它是 DRY 并隐藏在 TestUtil 中。
One last note, beware the embedded hamcrest issues in JUnit 4.10. With Maven, I recommend importing hamcrest-library first and then JUnit 4.11 (now 4.12) and exclude hamcrest-core from JUnit just for good measure:
最后一点,请注意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>
Edit: Sept 1, 2017 - Per some of the comments, I updated my answer to show my Mockito dependency, my imports in the test util, and a junit that is running green as of today:
编辑:2017 年 9 月 1 日 - 根据一些评论,我更新了我的答案以显示我的 Mockito 依赖项、我在测试 util 中的导入以及截至今天运行绿色的 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);
}
}
}
回答by rogerdpack
If you just want to "match" against a particular Map, you can use some of the answers above, or a custom "matcher" that extends Map, or an ArgumentCaptor, like this:
如果您只想“匹配”特定的 Map,您可以使用上面的一些答案,或者扩展 Map 的自定义“匹配器”或 ArgumentCaptor,如下所示:
ArgumentCaptor<Map> argumentsCaptured = ArgumentCaptor.forClass(Map.class);
verify(mock, times(1)).method((Map<String, String>) argumentsCaptured.capture());
assert argumentsCaptured.getValue().containsKey("keyname");
// .getValue() will be the Map it called it with.
See also more answers here: Verify object attribute value with mockito
另请参阅此处的更多答案:使用 mockito 验证对象属性值