官方資源
官方網(wǎng)站
版本介紹
還在使用 Mockito 1.x?看看 Mockito 2 有哪些新功能侥猬!Mockito 3 沒有引入任何破壞性的 API 變動(dòng),但現(xiàn)在需要 Java 8 而不是 Mockito 2 的 Java 6垒酬。 Mockito 4 刪除了過時(shí)的 API黎泣。Mockito 5 將默認(rèn) mockmaker 改為 mockito-inline绊含,現(xiàn)在需要 Java 11。一次只支持一個(gè)主要版本,而且不會(huì)向舊版本回傳更改內(nèi)容册着。
項(xiàng)目源碼
https://github.com/mockito/mockito
[圖片上傳失敗...(image-1c7a85-1704596577351)]
開發(fā)指南
添加maven依賴
這將在Maven項(xiàng)目中添加Mockito核心庫的依賴關(guān)系拴孤,并限定其范圍為測試(<scope>test</scope>
)。這樣甲捏,您就可以在單元測試中使用Mockito框架來模擬對象和驗(yàn)證行為了演熟。請注意,您需要根據(jù)您的實(shí)際需求調(diào)整版本號司顿。
<!-- 添加 Mockito 依賴 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
獲得 Mockito 的推薦方法是使用自己喜歡的構(gòu)建系統(tǒng)聲明對 "mockito-core "庫的依賴绽媒。使用 Gradle 可以做到這一點(diǎn):
添加Gradle依賴
repositories { mavenCentral() }
dependencies { testImplementation "org.mockito:mockito-core:3.+" }}
Maven 用戶可以聲明對 mockito-core 的依賴。Mockito 會(huì)將每次更改作為 -SNAPSHOT 版本發(fā)布到公共 Sonatype 資源庫免猾。進(jìn)行手動(dòng)依賴關(guān)系管理的用戶可直接從 Maven Central 下載 jar是辕。使用手動(dòng)依賴關(guān)系管理的傳統(tǒng)版本可使用 1.* "mockito-all" 發(fā)行版。該發(fā)行版在 Mockito 2.* 中已停用猎提。
mockito需要junit配合使用
需要將JUnit和Mockito結(jié)合在一起使用來進(jìn)行Java單元測試获三。在添加Mockito依賴之前,請確保您的項(xiàng)目中已經(jīng)包含了JUnit的依賴锨苏。
<dependencies>
<!-- 添加 JUnit 依賴 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 添加 Mockito 依賴 -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
</dependencies>
在上面的配置中疙教,我們添加了JUnit和Mockito的依賴關(guān)系,并將其范圍都設(shè)置為測試(<scope>test</scope>
)伞租。這樣贞谓,就可以使用JUnit來運(yùn)行測試,并使用Mockito進(jìn)行對象模擬和行為驗(yàn)證葵诈。請確保根據(jù)您的實(shí)際需求調(diào)整JUnit和Mockito的版本號裸弦。
導(dǎo)入靜態(tài)資源
為了使測試類中的代碼更簡潔和易讀,您可以通過靜態(tài)導(dǎo)入來引入Mockito和JUnit的一些靜態(tài)資源作喘。這樣可以減少在測試類中使用這些資源時(shí)的冗余代碼
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
mockito的方法
驗(yàn)證互動(dòng)
下面是對應(yīng)相關(guān)的案例代碼:
@Test
public void verify_behaviour(){
//模擬創(chuàng)建一個(gè)List對象
List mock = mock(List.class);
// 或者使用 Mockito 4.10.0+ 時(shí)更簡單
List mock = mock()理疙;
// 使用 mock 對象 - 它不會(huì)拋出任何 "意外交互 "異常
mock.add(1);
mock.clear();
//驗(yàn)證add(1)和clear()行為是否發(fā)生
// 選擇性的、明確的泞坦、可讀性高的驗(yàn)證
verify(mock).add(1);
verify(mock).clear();
}
詳細(xì)分析:使用mock()模擬對象
舉一個(gè)簡單的案例去模型mock數(shù)據(jù)效果窖贤,mock可以模擬各種各樣的對象,替代真正的對象做出希望的響應(yīng)贰锁。
//模擬LinkList的一個(gè)對象
LinkedList mockdedList = mock(LinkedList.class);
//此時(shí)條用get方法赃梧,會(huì)返回null,因?yàn)檫€沒有對方法調(diào)用的返回值做模擬豌熄。
System.out.printlin(mockedList.get(99));
模擬方法調(diào)用的返回值
mock對象被調(diào)用時(shí)的返回值
當(dāng)我們需要模擬獲取第一個(gè)元素并返回字符串“first”時(shí)授嘀,可以使用Mockito進(jìn)行樁模擬(stub)。樁模擬是指為特定的方法調(diào)用配置返回固定值的行為房轿。在官方文檔中粤攒,這種行為被稱為stub(存根)。通過使用樁模擬囱持,我們可以模擬本地對象以屏蔽對遠(yuǎn)程主機(jī)上對象的調(diào)用夯接。
when(mockedList.get(0).thenReturn("first"));
// 此時(shí)打印輸出first
System.out.println(mockedList.get(0));
詳細(xì)分析:模擬,方法調(diào)用拋出異常
// 模擬獲取第二個(gè)元素時(shí)纷妆,拋出RuntimeException
when(mockedList.get(1)).thenThrow(new RuntimeException);
// 此時(shí)拋出RuntimeException異常
System.out.println(mockedList.get(1));
// 沒有返回值類型的方法也可以模擬異常拋出:
doThrow(new RuntimeException()).when(mockedList).clear();
詳細(xì)分析:模擬調(diào)用方法時(shí)的參數(shù)匹配
anyInt()
是Mockito中的一個(gè)匹配器盔几,它用于匹配任何傳入的int
參數(shù)。這意味著無論傳入的參數(shù)是什么值掩幢,都將返回"element"逊拍。
when(mockedList.get(anyInt())).thenReturn("element");
// 此時(shí)打印是element
System.out.println(mockedList.get(99));
可以這樣描述anyInt()的作用:它用于接受任意的int
參數(shù),并將其值忽略际邻,返回固定的字符串"element"芯丧。
詳細(xì)分析:模擬方法調(diào)用次數(shù)
// 調(diào)用add一次
mockedList.add("once");
// 驗(yàn)證add方法是否被調(diào)用了一次,兩種寫法效果一樣
verify(mockedList)add("once");
verify(mockedList,times(1)).add("once");
可以使用Mockito中的atLeast(int i)
和atMost(int i)
來驗(yàn)證方法被調(diào)用的最小和最大次數(shù)限制世曾。
-
atLeast(int i)
用于驗(yàn)證方法至少被調(diào)用了i
次 -
atMost(int i)
用于驗(yàn)證方法最多被調(diào)用了i
次缨恒。
可以精確地控制方法被調(diào)用的次數(shù),并進(jìn)行相應(yīng)的驗(yàn)證轮听。通過使用這些方法骗露,可以更準(zhǔn)確地?cái)嘌苑椒ǖ恼{(diào)用次數(shù)是否符合預(yù)期。
詳細(xì)分析:驗(yàn)證被測試類是否正確工作血巍,使用verify()
在默認(rèn)情況下萧锉,對于所有未被樁模擬過的有返回值的方法,Mockito會(huì)返回相應(yīng)的默認(rèn)值述寡。對于基本數(shù)據(jù)類型柿隙,如int
,默認(rèn)值是0鲫凶;對于布爾類型优俘,默認(rèn)值是false
;對于其他對象類型掀序,默認(rèn)值是null
帆焕。
mock對象會(huì)覆蓋整個(gè)被mock的對象,因此沒有stub的方法只能返回默認(rèn)值不恭。重復(fù)stub兩次叶雹,則以第二次為準(zhǔn),如下將返回”second“换吧。
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(0)).thenReturn("second");
下面這種形式表示第一次調(diào)用返回”first“折晦,第二次調(diào)用返回”second“,可以寫n多個(gè)
when(mockedList.get(0)).thenReturn("first").thenReturn("second");
如果實(shí)際調(diào)用次數(shù)超過了stub過的次數(shù)沾瓦,則會(huì)一直返回最后一次stub的值满着,如上例谦炒,第三次調(diào)用get(0),則返回 ”second“驗(yàn)證方法被調(diào)用特定的次數(shù)。
驗(yàn)證add方法被調(diào)用了兩次
verify(mockedList,times(2)).add("2");
驗(yàn)證add方法致至少被調(diào)用一次
verify(mockedList.atLeastOnce()).add("2");
驗(yàn)證add方法至少被調(diào)用兩次
verify(mockedList,atLeast(2)).add("2");
驗(yàn)證add方法最大被調(diào)用5次
verify(mockedList,atMost(5)).add("2");
驗(yàn)證add方法從未被調(diào)用
找到冗余的調(diào)用风喇,使用never();
verify(mockedList,never()).add("2");
模擬所期望的結(jié)果
為了模擬獲取和檢索數(shù)據(jù)的行為宁改,我們可以使用Mockito來進(jìn)行對象模擬。通過模擬數(shù)據(jù)獲取和檢索的過程魂莫,我們可以更輕松地編寫和執(zhí)行單元測試雀摘。
// 你可以模擬具體的類翘鸭,而不僅僅是接口
LinkedList mockedList = mock(LinkedList.class);
// 或者使用 Mockito 4.10.0+ 更簡單
// LinkedList mockedList = mock();
// 在實(shí)際執(zhí)行前出現(xiàn)存根
when(mockedList.get(0)).thenReturn("first")榛搔;
// 下面將打印 "first
System.out.println(mockedList.get(0))轧简;
// 下面打印 "null"括授,因?yàn)?get(999) 沒有被存根化
System.out.println(mockedList.get(999))脆炎;
模擬所期望的結(jié)果
@Test
public void when_thenReturn(){
//mock一個(gè)Iterator類
Iterator iterator = mock(Iterator.class);
//預(yù)設(shè)當(dāng)iterator調(diào)用next()時(shí)第一次返回hello,第n次都返回world
when(iterator.next()).thenReturn("hello").thenReturn("world");
//使用mock的對象
String result = iterator.next() + " " + iterator.next() + " " + iterator.next();
//驗(yàn)證結(jié)果
assertEquals("hello world world",result);
}
模擬方法體拋出異常
案例一
在@Test(expected = IOException.class)
注解中指定了期望的異常類型為IOException
鞋邑,用于斷言在測試代碼中是否拋出了該異常最易,通過指定expected
注解和模擬對象的預(yù)設(shè)行為,我們可以斷言在調(diào)用close()
方法時(shí)是否會(huì)拋出IOException
異常炫狱。
@Test(expected = IOException.class)
public void when_thenThrow() throws IOException {
OutputStream outputStream = mock(OutputStream.class);
OutputStreamWriter writer = new OutputStreamWriter(outputStream);
//預(yù)設(shè)當(dāng)流關(guān)閉時(shí)拋出異常
doThrow(new IOException()).when(outputStream).close();
outputStream.close();
}
使用doThrow()
方法配置了當(dāng)outputStream
的close()
方法被調(diào)用時(shí)拋出IOException
異常的預(yù)設(shè)行為藻懒。,調(diào)用outputStream
的close()
方法视译,觸發(fā)異常拋出嬉荆。
案例二
在@Test(expected = RuntimeException.class)
注解中指定了期望的異常類型為RuntimeException
,用于斷言在測試代碼中是否拋出了該異常酷含。
@Test(expected = RuntimeException.class)
public void doThrow_when(){
List list = mock(List.class);
doThrow(new RuntimeException()).when(list).add(1);
list.add(1);
}
使用doThrow()
方法配置了當(dāng)list
的add(1)
方法被調(diào)用時(shí)拋出RuntimeException
異常的預(yù)設(shè)行為鄙早,調(diào)用list
的add(1)
方法,觸發(fā)異常拋出椅亚。通過指定expected
注解和模擬對象的預(yù)設(shè)行為限番,我們可以斷言在調(diào)用add(1)
方法時(shí)是否會(huì)拋出RuntimeException
異常。
驗(yàn)證執(zhí)行順序
下面代碼用于驗(yàn)證在特定順序下方法的執(zhí)行狀況呀舔,使用inOrder.verify()方法按順序驗(yàn)證了方法的調(diào)用弥虐。
@Test
public void verification_in_order(){
List list = mock(List.class);
List list2 = mock(List.class);
list.add(1);
list2.add("hello");
list.add(2);
list2.add("world");
//將需要排序的mock對象放入InOrder
InOrder inOrder = inOrder(list,list2);
//下面的代碼不能顛倒順序,驗(yàn)證執(zhí)行順序
inOrder.verify(list).add(1);
inOrder.verify(list2).add("hello");
inOrder.verify(list).add(2);
inOrder.verify(list2).add("world");
}
驗(yàn)證順序
先驗(yàn)證list的add(1)方法被調(diào)用媚赖,然后驗(yàn)證list2的add("hello")方法被調(diào)用霜瘪,接著驗(yàn)證list的add(2)方法被調(diào)用,最后驗(yàn)證list2的add("world")方法被調(diào)用惧磺。
通過使用InOrder對象颖对,我們可以驗(yàn)證方法的執(zhí)行順序是否符合預(yù)期。如果按照預(yù)期順序執(zhí)行磨隘,則測試將成功通過缤底。如果順序不符合預(yù)期顾患,測試將失敗。
未完待續(xù)
敬請期待:【Java技術(shù)深入解析】「核心技術(shù)提升」最流行的Java模擬框架Mockito入門指南(進(jìn)階學(xué)習(xí)案例)个唧。