「測試」Mock之Mockito認(rèn)識之旅

一.前言

??關(guān)于這篇文章的起源,是第三次思沃大講堂的作業(yè)的題目中,有這樣一段話

把前兩問做的類集成起來,寫一個集成的單元測試,寫一個集成測試。

問題來了,集成的單元測試和集成測試有什么區(qū)別呢?
??集成測試(Integration Testing):是在單元測試的基礎(chǔ)上,將所有模塊按照概要設(shè)計要求組裝成為一個子系統(tǒng)或者系統(tǒng),進(jìn)行集成測試。一些模塊雖然能夠單獨工作,但并不能保證連接起來也能正常的工作,程序在某些局部反映不出來的問題,在全局上很可能暴漏出來,因此集成測試十分必要。
??集成的單元測試:按字面意思的理解,就是對該集成類進(jìn)行單元測試。單元測試就是對已經(jīng)實現(xiàn)的軟件最小單元進(jìn)行測試以保證構(gòu)成軟件系統(tǒng)的各個單元的質(zhì)量。這么說來,在此處集成的單元測試和集成測試并無區(qū)別嗎?
??No~ 區(qū)別還是有的,集成的單元測試,首先是個單元測試,然后是對集成類進(jìn)行單元測試,也就是說只測試該類的邏輯,而不用關(guān)注他所依賴的類是否正確實現(xiàn)。如何不關(guān)注依賴類的是否正確實現(xiàn)呢?這就是接下來我要介紹的Mock啦,這得感謝我的Buddy,讓我對Mock有了直白的認(rèn)識,然后才關(guān)注Mock,從而進(jìn)一步了解。


二.為什么需要mock

??我們在做測試的時候,往往會發(fā)現(xiàn)我們要測試的類或方法會引用很多外部依賴的對象,而我們沒法控制這些外部依賴的對象,為了解決這個問題,我們需要用到Mock來模擬這些外部依賴的對象,從而控制它們。舉個例子,service調(diào)用dao,即service依賴dao,我們可以用mock來模擬真實的dao調(diào)用,從而達(dá)到測試service的目的。
??模擬對象(Mock Object)可以取代真實對象的位置,用于測試一些與真實對象進(jìn)行交互或依賴于真實對象的功能,模擬對象背后的目的就是創(chuàng)建一個輕量級的,可以控制的對象來代替測試中需要的真實對象,模擬真實對象的行為和功能。

mock對象使用范疇
1.真實對象具有不可確定的行為,產(chǎn)生不可預(yù)測的效果。
2.真實對象很難被創(chuàng)建的。
3.真實對象的某些行為很難被觸發(fā)。
4.真實對象實際上還不存在的。


三.常見的mock框架
  • jmock:通過mock對象來模擬一個對象的行為,從而隔離開我們不關(guān)心的其他對象,使得測試變得簡單。缺點:在執(zhí)行前記錄期望行為,顯得很繁瑣。
  • Mockito:Mockito通過在執(zhí)行后校驗?zāi)男┖瘮?shù)已經(jīng)被調(diào)用,消除了對期望行為的需要,API非常簡潔。缺點:對于靜態(tài)函數(shù)、構(gòu)造函數(shù)、私有函數(shù)等還是無能為力。
  • powermock:PowerMock是在Mockito的基礎(chǔ)上做出的擴(kuò)展。通過提供定制的類加載器以及一些字節(jié)碼篡改技巧的應(yīng)用,PowerMock 實現(xiàn)了對靜態(tài)方法、構(gòu)造方法、私有方法以及 Final 方法的模擬支持,對靜態(tài)初始化過程的移除等強大的功能。缺點:會對字節(jié)碼篡改,即測試時的字節(jié)碼與平時編譯出來的字節(jié)碼是不一樣的,而很多統(tǒng)計單元測試覆蓋率的插件是以字節(jié)碼來統(tǒng)計的,所以PowerMock編寫的測試程序不能被統(tǒng)計進(jìn)覆蓋率。

推薦Mockito和powermock


四.Mockito簡單介紹

一般使用Mockito需要執(zhí)行以下步驟:
1.模擬并替換測試代碼中外部依賴。
2.執(zhí)行測試代碼。
3.驗證測試代碼是否被正確的執(zhí)行


Mock執(zhí)行步驟.png

使用注解(@Mock、@InjectMocks等)的話,必須實例化mock對象,有兩種方式實例化mock對象:

  • @RunWith(MockitoJUnitRunner.class)
  • MockitoAnnotations.initMocks(this)

當(dāng)我們需要配置某個方法的返回值時,Mockito提供了鏈?zhǔn)降腁PI供我們方便的調(diào)用:

  • when(mockObject.someMethod()).thenReturn(...)
    用來定義當(dāng)條件滿足時函數(shù)的返回值。
  • when(mockObject.someMethod()).thenReturn(...).thenReturn(...)
    用來定義多個返回值的情況。
  • doReturn(...).when(mockObject.someMethod())
  • when(mockObject.someMethod()).thenThrow(new Runtime
    Exception())
    執(zhí)行某方法時拋出異常。
  • doThrow(new RuntimeException()).when(mockObject.someMethod())
  • 對void方法進(jìn)行預(yù)期設(shè)定
    1.doNothing().when(mock.someMethod())
    2.doThrow(new RuntimeException()).when(mock.someMethod())
    3.doNothing().doThrow(new RuntimeException()).when(mock.someMethod())
  • Mockito會自動記錄自己的交互行為,可以用verify(…).methodXxx(…)語法來驗證Xxx()方法是否按照預(yù)期進(jìn)行了調(diào)用
    1.驗證調(diào)用次數(shù):verify(mockObject,times(n)).someMethod(argument);//n為被調(diào)用的次數(shù)
    2.驗證超時:verify(mockObject, timeout(100)).someMethod();
    3.既驗證調(diào)用次數(shù),又驗證是否超時:verify(mockObject, timeout(100).times(1)).someMethod();

需注意:

  • 對于final、static方法,Mockito 無法對其 when(…).thenReturn(…) 操作。

五.Mockito使用實例

以第三次思沃大講堂的作業(yè)為例。
問題描述:游戲開始后,系統(tǒng)會隨機(jī)給出一個四位,每位都不重復(fù)的數(shù)字作為答案。由用戶輸入自己猜測的 四個數(shù)字。 系統(tǒng)會將兩個數(shù)字進(jìn)行對比,并給形出xAxB的提示, 比如”2A1B”。 如果數(shù)字猜對而且位置也對,就是一個A。 如果數(shù)字猜對但位置不對,就是一個B。 例如:系統(tǒng)給出”1234”,用戶輸入”1234” 返回”4A0B” 系統(tǒng)給出”1234”,用戶輸入”4321” 返回”0A4B”。
CompareNumber類:實現(xiàn)比較。只有一個函數(shù),該函數(shù)接受兩個參數(shù),一個是答案,一個是用戶輸 入的四位數(shù)。返回值是xAxB的字符串 。
AnswerGenerator類:生成隨機(jī)的四位無重復(fù)位數(shù)字。只有一個函數(shù),返回一個四位,每位都不重復(fù)隨機(jī)數(shù)。
Guess類:只有一個函數(shù),只有一個參數(shù)。把前兩問做的類集成起來。
GuessUnitTest類:對Guess類寫單元測試(即文首所說的集成的單元測試)。

public class Guess {
    private CompareNumber compareNumber = new CompareNumber();
    private AnswerGenerator answerGenerator = new AnswerGenerator();
    private int answer=answerGenerator.generatorFourDigits();

    public String guessTheDigit(int guessDigit){
        String result = compareNumber.compareToAnswer(answer,guessDigit);
        return result;
    }
}
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class GuessUnitTest {

    @InjectMocks
    private Guess guess = new Guess();

    @Mock
    private CompareNumber mockCompareNumber = new CompareNumber();

    @Mock
    private AnswerGenerator mockAnswerGenerator = new AnswerGenerator();

    @Before
    public void init() throws NoSuchFieldException, IllegalAccessException {
        /*返回guess已聲明字段answer*/
        Field f = guess.getClass().getDeclaredField("answer");
        /*值為 true 則指示反射的對象在使用時應(yīng)該取消 Java 語言訪問檢查*/
        f.setAccessible(true);
        /*將指f對象表示的字段answer設(shè)置為指定的值,即1234。*/
        f.set(guess, 1234);
    }

    @Test
    public void test_4A0B() {
        when(mockCompareNumber.compareToAnswer(1234, 1234)).thenReturn("4A0B");
        String result = guess.guessTheDigit(1234);
        assertTrue("4A0B".equals(result));
    }

    @Test
    public void test_0A4B() {
        when(mockCompareNumber.compareToAnswer(1234, 4321)).thenReturn("0A4B");
        String result = guess.guessTheDigit(4321);
        assertTrue("0A4B".equals(result));
    }

    @Test
    public void test_2A2B() {
        when(mockCompareNumber.compareToAnswer(1234, 1432)).thenReturn("2A2B");
        String result = guess.guessTheDigit(1432);
        assertTrue("2A2B".equals(result));
    }
}

說明
@Mock:創(chuàng)建一個mock對象(模擬對象)。
@InjectMock:創(chuàng)建一個實例,@Mock注解創(chuàng)建的模擬對象將被注入到該實例中。
@Before:Junit注解,在每個測試執(zhí)行之前必須執(zhí)行的代碼。
@Test:Junit注解,標(biāo)明是一個測試方法。

Junit4常用注解

  • @Before:初始化方法,在任何一個測試執(zhí)行之前必須執(zhí)行的代碼。
  • @After:釋放資源,在任何測試執(zhí)行之后需要進(jìn)行的收尾工作。
  • @Test:測試方法,表明這是一個測試方法。在Junit中將會自動被執(zhí)行。
  • @Ignore:忽略的測試方法,含義為“某些方法尚未完成,暫不參與此次測試”。測試結(jié)果提示你有幾個測試被忽略,而不是失敗。
  • @BeforeClass:針對所有測試,在所有測試方法執(zhí)行前執(zhí)行一次。
  • @AfterClass:針對所有測試,在所有測試方法執(zhí)行結(jié)束后執(zhí)行一次。

在Junit4中,單元測試用例的執(zhí)行順序:


單元測試用例的執(zhí)行順序.png

每個測試方法的執(zhí)行順序:


測試方法的執(zhí)行順序.png

六.Mockito學(xué)習(xí)資料

Mockito官網(wǎng):http://site.mockito.org/
Mockito官方文檔:https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/Mockito.html
Mockito中文文檔:http://blog.csdn.net/bboyfeiyu/article/details/52127551
Mockito Github:https://github.com/mockito/mockito

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,702評論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,615評論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,606評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,044評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,826評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,227評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,307評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,447評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,992評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,807評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,001評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,550評論 5 361
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,243評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,667評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,930評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,709評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,996評論 2 374

推薦閱讀更多精彩內(nèi)容