一款優(yōu)秀的Java模擬框架:Mockito
?愿你越努力越幸運(yùn)?

日常的開發(fā)中,習(xí)慣性地寫完需求代碼后,嗖的一聲運(yùn)行一個(gè)main函數(shù)或?qū)憥讉€(gè)簡(jiǎn)單的JUnit的單元測(cè)試來跑功能點(diǎn),多寫幾個(gè)單元測(cè)試過沒有問題就可以上線了(其實(shí)這樣是不規(guī)范的),對(duì)于需要對(duì)接第三方或者驗(yàn)證不同條件的代碼分支邏輯時(shí),這種方法就會(huì)變得不可取,因?yàn)闃I(yè)務(wù)邏輯中需要依賴其他的接口,而這時(shí)候所依賴的接口還沒有準(zhǔn)備好,那我們應(yīng)該怎么辦呢?
這時(shí)候該Mockito派上用場(chǎng)了,一方面使用Mockito可以屏蔽依賴接口并返回Mock數(shù)據(jù),使得雙方的開發(fā)得以同步進(jìn)行(確定接口的協(xié)議)編碼,另一方面使用Mockito驗(yàn)證業(yè)務(wù)邏輯,當(dāng)日后更改到某處代碼即可回歸測(cè)試用例看改動(dòng)是否覆蓋到所有的測(cè)試點(diǎn),因此使用Mockito不單單能保證代碼的質(zhì)量,更能提高代碼維護(hù)性、提前發(fā)現(xiàn)代碼的bug。
Mock四要素
什么是Mock
在軟件開發(fā)的世界之外, "Mock"一詞是指模仿或者效仿。 因此可以將“Mock”理解為一個(gè)替身、替代者,在軟件開發(fā)中提及"Mock",通常理解為模擬對(duì)象或者Fake
為什么需要Mock
Mock是為了解決units、代碼分層開發(fā)之間由于耦合而難于被測(cè)試的問題,所以mock object是單元測(cè)試的一部分
Mock的好處是什么
提前創(chuàng)建測(cè)試,提高代碼質(zhì)量、TDD(測(cè)試驅(qū)動(dòng)開發(fā))
并行工作
創(chuàng)建一個(gè)驗(yàn)證或者演示程序,為無法訪問的資源編寫測(cè)試
什么是Mockito
Mockito是一個(gè)非常優(yōu)秀的模擬框架,可以使用它簡(jiǎn)潔的API來編寫漂亮的測(cè)試代碼,它的測(cè)試代碼可讀性高同時(shí)會(huì)產(chǎn)生清晰的錯(cuò)誤日志。
1、引入依賴:
<dependency><groupId>org.mockitogroupId><artifactId>mockito-coreartifactId><version>3.3.3version><scope>testscope>dependency>
注意:Mockito 3.X的版本使用了JDK8的API,但與2.X的版本并沒有太大的變化。
2、在測(cè)試類中添加@RunWith注解,并制定Runner的類,即MockitoJUnitRunner
(MockitoJUnitRunner.class)public class MockitoDemoTest {????//注入依賴的資源對(duì)象private MockitoTestService mockitoTestService;public void before(){MockitoAnnotations.initMocks(this);}}
從代碼中可觀察到,使用@Mock標(biāo)識(shí)對(duì)象是被Mock的,同時(shí)在初始化前置執(zhí)行MockitoAnnotations.initMocks(this)告訴框架生Mock相關(guān)注解生效。
3、驗(yàn)證對(duì)象的行為Verify
@Testpublic void testVerify(){//創(chuàng)建mockList mockedList = mock(List.class);mockedList.add("1");mockedList.clear();//驗(yàn)證list調(diào)用過add的操作行為verify(mockedList).add("1");//驗(yàn)證list調(diào)用過clear的操作行為verify(mockedList).clear();//使用內(nèi)建anyInt()參數(shù)匹配器,并存根when(mockedList.get(anyInt())).thenReturn("element");System.out.println(mockedList.get(2)); //此處輸出為elementverify(mockedList).get(anyInt());}
4、存根—stubbing
stubbing完全是模擬一個(gè)外部依賴,用來提供測(cè)試時(shí)所需要的測(cè)試數(shù)據(jù)
public void testStub(){//可以mock具體的類,而不僅僅是接口LinkedList mockedList = mock(LinkedList.class);//存根(stubbing)when(mockedList.get(0)).thenReturn("first");when(mockedList.get(1)).thenThrow(new RuntimeException());//下面會(huì)打印 "first"System.out.println(mockedList.get(0));//下面會(huì)拋出運(yùn)行時(shí)異常System.out.println(mockedList.get(1));//下面會(huì)打印"null" 因?yàn)間et(999)沒有存根(stub)System.out.println(mockedList.get(999));doThrow(new RuntimeException()).when(mockedList).clear();//下面會(huì)拋出 RuntimeException:mockedList.clear();}
存根(stub)可以覆蓋:例如測(cè)試方法可以覆蓋通用存
一旦做了存根方法將總是返回存根的值,無論這個(gè)方法被調(diào)用多少次
5、存根的連續(xù)調(diào)用
@Testpublic void testStub() {when(mock.someMethod("some arg")).thenThrow(new RuntimeException())????.thenReturn("foo");mock.someMethod("some arg"); //第一次調(diào)用:拋出運(yùn)行時(shí)異常//第二次調(diào)用: 打印 "foo"System.out.println(mock.someMethod("some arg"));//任何連續(xù)調(diào)用: 還是打印 "foo" (最后的存根生效).System.out.println(mock.someMethod("some arg"));//可供選擇的連續(xù)存根的更短版本:when(mock.someMethod("some arg")).thenReturn("one", "two", "three");when(mock.someMethod(anyString())).thenAnswer(new Answer() {Object answer(InvocationOnMock invocation) {Object[] args = invocation.getArguments();Object mock = invocation.getMock();return "called with arguments: " + args;}});// "called with arguments: fooSystem.out.println(mock.someMethod("foo"));}
6、參數(shù)匹配器
@Testpublic void testArugument{//使用內(nèi)建anyInt()參數(shù)匹配器when(mockedList.get(anyInt())).thenReturn("element");System.out.println(mockedList.get(999)); //打印 "element"//同樣可以用參數(shù)匹配器做驗(yàn)證verify(mockedList).get(anyInt());//注意:如果使用參數(shù)匹配器,所有的參數(shù)都必須通過匹配器提供。verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));//上面是正確的 - eq(0也是參數(shù)匹配器),而下面的是錯(cuò)誤的verify(mock).someMethod(anyInt(), anyString(), "third argument");}
7、驗(yàn)證精確調(diào)用次數(shù)/至少X次/從不
@Testpublic void testVerify{ListmockedList = new ArrayList(); mockedList.add("once");mockedList.add("twice");mockedList.add("twice");mockedList.add("three times");mockedList.add("three times");mockedList.add("three times");//下面兩個(gè)驗(yàn)證是等同的 - 默認(rèn)使用times(1)verify(mockedList).add("once");verify(mockedList, times(1)).add("once");verify(mockedList, times(2)).add("twice");verify(mockedList, times(3)).add("three times");//使用using never()來驗(yàn)證. never()相當(dāng)于 times(0)verify(mockedList, never()).add("never happened");//使用 atLeast()/atMost()來驗(yàn)證verify(mockedList, atLeastOnce()).add("three times");verify(mockedList, atLeast(2)).add("five times");verify(mockedList, atMost(5)).add("three times");}
8、驗(yàn)證調(diào)用順序
@Testpublic void testOrder(){// A. 單個(gè)Mock,方法必須以特定順序調(diào)用List singleMock = mock(List.class);//使用單個(gè)MocksingleMock.add("was added first");singleMock.add("was added second");//為singleMock創(chuàng)建 inOrder 檢驗(yàn)器InOrder inOrder = inOrder(singleMock);//確保add方法第一次調(diào)用是用"was added first",然后是用"was added second"inOrder.verify(singleMock).add("was added first");inOrder.verify(singleMock).add("was added second");}
今天主要講Mockito的常用方式,下一篇將講解Spring Boot如何集成Mockito、JUnit、PowerMock做Mock單元測(cè)試。
—————END—————
推薦閱讀:
最近面試BAT,整理一份面試資料《Java面試BAT通關(guān)手冊(cè)》,覆蓋了Java核心技術(shù)、JVM、Java并發(fā)、SSM、微服務(wù)、數(shù)據(jù)庫、數(shù)據(jù)結(jié)構(gòu)等等。
獲取方式:點(diǎn)“在看”,關(guān)注公眾號(hào)并回復(fù)?666?領(lǐng)取,更多內(nèi)容陸續(xù)奉上。
明天見(??ω??)??
