我需要根据特定的键值从模拟对象发送一个特定的值。
来自具体课程:
map.put("xpath", "PRICE");
search(map);
从测试用例:
IOurXMLDocument mock = mock(IOurXMLDocument.class);
when(mock.search(.....need help here).thenReturn("$100.00");
如何为此键值对模拟此方法调用?
答案 0 :(得分:17)
我发现这试图解决类似的问题,创建一个带有Map参数的Mockito存根。我不想为有问题的地图编写自定义匹配器,然后我找到了一个更优雅的解决方案:使用hamcrest-library中的其他匹配器与mockito' s 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中。
最后一点,请注意embedded hamcrest issues in JUnit 4.10。对于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依赖关系,我在test 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);
}
}
}
答案 1 :(得分:7)
如果您只想与特定地图“匹配”,您可以使用上面的一些答案,或者使用扩展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.
另请参阅此处的更多答案:Verify object attribute value with mockito
答案 2 :(得分:3)
这不起作用吗?
Map<String, String> map = new HashMap<String, String>();
map.put("xpath", "PRICE");
when(mock.search(map)).thenReturn("$100.00");
Map
参数的行为应与其他参数相同。
答案 3 :(得分:2)
看起来像你需要的是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 "$100.00";
} else if ("PRODUCTNAME".equals(value)) {
return "Candybar";
} else {
return null;
}
}
});
但似乎更好的想法是不使用原始Map
作为搜索方法的参数 - 您可以将此地图转换为具有price
和productName
属性的pojo。只是一个想法:)
答案 4 :(得分:0)
晚参加聚会 11 年,但 org.mockito.Matchers#anyMapOf
为我工作。在 OP 的示例中,它将是:
when(mock.search(anyMapOf(String.class, String.class))).thenReturn("$100.00");