我需要根據特定鍵值從模擬對象發送特定值。使用Mockito,我如何匹配地圖的鍵值對?
從具體類:
map.put("xpath", "PRICE");
search(map);
從測試情況:
IOurXMLDocument mock = mock(IOurXMLDocument.class);
when(mock.search(.....need help here).thenReturn("$100.00");
如何嘲笑這個方法調用此鍵值對?
我需要根據特定鍵值從模擬對象發送特定值。使用Mockito,我如何匹配地圖的鍵值對?
從具體類:
map.put("xpath", "PRICE");
search(map);
從測試情況:
IOurXMLDocument mock = mock(IOurXMLDocument.class);
when(mock.search(.....need help here).thenReturn("$100.00");
如何嘲笑這個方法調用此鍵值對?
這不工作?
Map<String, String> map = new HashMap<String, String>();
map.put("xpath", "PRICE");
when(mock.search(map)).thenReturn("$100.00");
的Map
參數都應當遵循的其他參數相同。
好像你需要的是一個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
作爲參數傳遞給你的搜索方法 - 你也許可以改變這個地圖與一個POJO price
和productName
屬性。只是一個想法:)
我發現這試圖解決一個類似的問題創建一個Map參數的Mockito存根。我不想寫的地圖有問題的自定義匹配,然後我發現了一個更優雅的解決方案:使用額外的匹配在hamcrest-library用的Mockito的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庫,然後再導入JUnit 4.11(現在爲4。12),並排除hamcrest核心從JUnit的只是良好的措施:
<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依賴,我進口測試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);
}
}
}
這是一個方便的解決方案。奇怪的是,我得到了一個「錯誤的參數類型,期望Map
@PatrickM您是否設法以更簡潔的方式解決了泛型問題?從上面我有完全相同的問題,但無法更改集合通用類型... – 2016-02-10 13:50:16
@AndrewEells,不,我從來沒有找到解決方案。我從Hamcrest轉換開始使用AssertJ,因爲它的斷言似乎需要更少的類型強制。 – 2016-02-10 14:39:10
如果你只是想「匹配」針對特定地圖,你可以使用一些答案的上方,或自定義「匹配」擴展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
這正是我所期待的。謝謝。 – bitoiu 2014-11-12 16:30:03
還有就是缺少一個右括號。 – stefanglase 2010-04-05 21:49:54
IOurXMLDocument 代表我們不想爲我的單元測試調用的服務層。對於1種情況,我們用兩個不同的地圖值調用它兩次。相反,我想檢查這個值並返回一個固定的結果。因此,當應用程序代碼執行以下操作時: map.put(「xpath」,「PRODUCTNAME」); (mock.search(map))。thenReturn(「Candybar」); – Sean 2010-04-06 15:54:32
不應該是mock.search(eq(map))所以它檢查實際的地圖平等? – fikovnik 2015-11-27 21:50:06