在編寫單元測試的時候,爲了儘量的保證隔離性,咱們時常須要對某些不容易構造或者不容易獲取或者對外部環境有依賴的對象,用一個虛擬的對象來建立以便於測試.假設你正在開發的的代碼中使用到了公司其餘部門的接口(經過RPC服務),當編寫單元測試的時候你可能爲了避免讓接口真的去調用rpc服務而mock一個接口的對象,最原始的方式是本身手工編寫一個該接口的實現類,而且在單元測試的時候注入這個對象,而使用Mockito則可讓咱們方便地建立和配置mock對象,使用mockito能夠簡化對外部環境的依賴.redis
這裏咱們以一個操做Redis的工具類來舉例,下面是代碼:函數
public class RedisUtil { @Autowired private RedisOperations<String, String> redisTemplate; public boolean checkKeyExists(String key) { return redisTemplate.hasKey(key); } public void setValueByKey(String key, String value) { redisTemplate.opsForValue().set(key, value); }
public String getValueByKey(String key) { return redisTemplate.opsForValue().get(key); } public List<String> getMutiValuesByList(List<String> keys){ return redisTemplate.opsForValue().multiGet(keys); } public void deleteKey(String key) { redisTemplate.delete(key); } public void increValue(String key,Long count){ redisTemplate.opsForValue().increment(key,count); } }
有兩種方式能夠方便的建立mock對象,第一種方式是工具
Mockito.mock(RedisUtil.class);
還有一種方式在注入時使用@mock註解:單元測試
@mock private RedisUtil redisUtil;
Tips:若是在代碼中頻繁的使用Mockito比較煩,能夠靜態導入package,如下例子所有默認已靜態導入Mockito包:測試
import static org.mockito.Mockito.*;
當對象被建立以後,就能夠對代碼中出現的方法進行自定義的交互,mock對象會記住這些交互,在單元測試的執行中碰到代碼中的對應方法會默認執行被你自定義的方法內容.spa
仍是以RedisUtil爲例,對方法設定返回值:.net
when(redisUtil.getValueByKey("key1")).thenReturn("value1");
when(redisUtil.getValueByKey("key2")).thenReturn("value2");
對方法設定返回自定義的異常信息:code
when(redisUtil.getValueByKey("key1")).thenThrow(new RuntimeException);
此外Mockito還支持迭代風格的返回值定義:xml
when(redisUtil.getValueByKey("key1")).thenReturn("value1").thenReturn("Value2");
即當方法第一次調用redis.getValueBykey("key1")時會返回value1,當再次被調用時則會返回value2.這裏須要注意的是,當後續再出現調用的時候返回值都會是value2,並且這種迭代風格的定義支持return和Throw的混搭,即你能夠控制在函數調用的第一次去拋出一個異常,而在函數調用的第二次繪製一個正常的值.對象
首先,測試中對於返回值爲void 的方法進行mock自己是沒有什麼效果的,Mockito有一個doNothing方法是void方法的默認返回:
doNothing().when(redisUtil).increValue(「key1",1L);
其實這裏使用doNothing來mock這個方法並無什麼意義,由於咱們mock一個方法的目的無非有兩個,第一,在某一中輸入環境中模擬返回咱們期待的返回值,第二就是當方法拋出異常時可以在咱們預期控制之下而不會致使單元測試失敗,所以對於返回值爲void的方法,咱們通常能夠不去mock它或者使用doThrow()來爲void函數打一個樁,當出現異常的時候mock他的異常返回,當不會有異常發生時,只須要在調用後,verify()一下,驗證方法的被調用次數便可.
verify(redisUtil,times(1)).increValue("key1",1L);
代碼中的times(1)表示一次,即代碼中increValue()返回被調用一次的時候可以經過,還能夠支持更加普遍的定義,
never():表示從未被調用
atleastOnce():表示至少被調用一次
atleast(3):表示至少被調用3次
atMost(7):表示最多被調用7次
這裏主要介紹一下內置的幾個參數匹配器,其實也很好理解,仍是那上面的redisUtil爲例,對於redisUtil.getValueByKey來講,我但願對於任意的key都返回同一個值,那就能夠這麼寫:
when(redisUtil.getValueByKey(anyString()).thenReturn("value1")
這樣在單元測試過程當中,對於任意的輸入參數,該方法都會返回value1,相同的類型還有不少anyLong(),anyInt(),anyList()等等
以上所講的對象都是mock對象,mock對象只能調用打樁方法,不能調用真實方法,使用Spy可讓咱們可以監視一個真實對象,既能夠對這個對象的某一個函數打樁返回咱們指望的值,也能夠去調用真實的方法,建立spy對象的方式和mock相似,不一樣的一點是spy須要傳一個真實對象而不是一個CLass對象.這裏以一個List爲例,
List spy = spy(new LinkedList()); when(spy.get(0)).thenReturn("value1"); doReturn("value2").when(spy).get(0);
上面第二行代碼,調用when(spy.get(0)),會去調用真實的方法,會拋出異常,第三行代碼則不會去調用真實方法,而返回value2.因此總結一下就是,當使用when去模擬返回值的時候,真是方法會被調用,而是用doReturn()去設置的話,則不會去執行真實方法.
須要注意在使用時應該儘可能避免使用spy.