mock單元測(cè)試 mockito實(shí)踐

1基本的使用步驟

1,環(huán)境使用mock和test包

2瞧柔,使用mockitoRule構(gòu)造mockito環(huán)境

@Rule

public MokitoRule rule = MockitoJUnit.rule();

注意這里的修飾符public猎塞,如果沒(méi)有這個(gè)修飾符的話使用mock測(cè)試會(huì)報(bào)錯(cuò)

?@Rule

?public ExpectedException thrown = ExpectedException.none();

可以選擇忽視拋出的異常?(我是這么理解的不知道是否正確)

3,對(duì)目標(biāo)測(cè)試類加@InjectMocks注解

4氮兵,對(duì)目標(biāo)測(cè)試類的依賴屬性使用@mock創(chuàng)建依賴

5,使用@test注解測(cè)試目標(biāo)類的方法執(zhí)行邏輯

2mock對(duì)象創(chuàng)建和目標(biāo)測(cè)試類的依賴注入

這里我還沒(méi)有看juint執(zhí)行的邏輯歹鱼,只是看了mock環(huán)境下獲取注解創(chuàng)建mock對(duì)象泣栈,并將mock的對(duì)向注入到@Injectmocks的目標(biāo)測(cè)試對(duì)象中去的邏輯。

1弥姻,測(cè)試前的準(zhǔn)備

使用的jar包版本是junit.junit.4.11,org.powermock.powermock-api-mockito.1.6.6

創(chuàng)建一個(gè)interface用于目標(biāo)測(cè)試類的filed的type南片。

public interface MyInterface { public void show(); }

創(chuàng)建一個(gè)目標(biāo)測(cè)試類,其中聲明filed的type為上面的interface的type

public class DoMainObject {

private MyInterface a;

private MyInterface b;

public void aShow(){a.show();}

public void bShow(){b.show();}

}

最后創(chuàng)建一個(gè)測(cè)試用例對(duì)象用于測(cè)試目標(biāo)測(cè)試類


public class MyMockTest {

@Rule public MockitoRule mockito = MockitoJUnit.rule();//創(chuàng)建mockito的rule

@Rule public ExpectedException thrown = ExpectedException.none();//忽視異常

@InjectMocks private DoMainObject doMainObject;//目標(biāo)測(cè)試類

@Mock private MyInterface a;//目標(biāo)測(cè)試類的field a

@Mock private MyInterface b;//目標(biāo)測(cè)試類的filed b

@Test public void testShow(){

doMainObject.aShow();

doMainObject.bShow();

}

}

2進(jìn)行debug測(cè)試

1測(cè)試類中加斷點(diǎn)debug

我從rule對(duì)象執(zhí)行的時(shí)候開(kāi)始追蹤庭敦,junit的運(yùn)行原理略過(guò)


2疼进,initmocks方法內(nèi)部


其中的testClass就是測(cè)試用例,MyMockTest的實(shí)例秧廉,annotationEngine是默認(rèn)的注解驅(qū)動(dòng)InjectingAnnotationEngine伞广,

這個(gè)方法的內(nèi)部獲取測(cè)試用例的type,獲取注解驅(qū)動(dòng)定血,并判斷是否是默認(rèn)的注解驅(qū)動(dòng)赔癌,可以自定義注解驅(qū)動(dòng)?(暫時(shí)我還辦不到)澜沟,之后注解驅(qū)動(dòng)執(zhí)行

3灾票, InjectingAnnotationEngine .process方法


InjectingAnnotationEngine.process 內(nèi)部只有兩個(gè)方法,從名字和其上的注釋可以知道

processIndependentAnnotations處理獨(dú)立的filed茫虽,其實(shí)就是測(cè)試用例中有@mock注解的filed刊苍,這里就是a和b既们。processInjectMocks處理依賴于獨(dú)立mock對(duì)象的filed,就是測(cè)試用例中有@InjectMocks注解的filed正什,依賴于mock對(duì)象的目標(biāo)測(cè)試類啥纸,這里就是DoMainObject,先看processInjectMocks

4婴氮, processIndependentAnnotations


入?yún)⒎謩e為測(cè)試用例的type斯棒,和instance,方法中只有一個(gè)循環(huán)主经,在循環(huán)的內(nèi)部處理三件事

1delegate.process(classContext, testInstance);委派對(duì)象處理@Mock荣暮,@Captor等注解,

2spyAnnotationEngine.process(classContext, testInstance);監(jiān)視注解驅(qū)動(dòng)處理@Spy注解

3獲取測(cè)試用例的父類罩驻,賦值給原來(lái)的變量

4如果Class的type為Object穗酥,跳出循環(huán)

這個(gè)方法就是先處理自己的獨(dú)立注解,然后去處理父類的獨(dú)立注解惠遏,如此往復(fù)直到父類為Object源類砾跃。

5delegate.process


這個(gè)方法參數(shù)還是Class的type和Class 的instance,

處理過(guò)程是獲取instance的所有field就是所有的屬性节吮,然后循環(huán)獲取filed的上的所有注解抽高,更具注解和field嘗試創(chuàng)建mock對(duì)象,這里最后的創(chuàng)建對(duì)象時(shí)使用cblib創(chuàng)建代理對(duì)象透绩,最后創(chuàng)建一個(gè)Setter對(duì)象將創(chuàng)建的cglib代理對(duì)象mock對(duì)象厨内,set進(jìn)instance的field中去,即完成了一個(gè)測(cè)試用例中的屬性的注入(spring的bean注解注入方式是不是也是如此呢渺贤,所有的基于注解的實(shí)現(xiàn)原理是否基本類似于此呢)

這里只關(guān)注兩個(gè)方法createMockFor和FieldSetter(testInstance, field).set(mock)

6, DefaultAnnotationEngine.?createMockFor

createMockFor方法的流程比較復(fù)雜请毛,


這個(gè)方法的內(nèi)部有兩個(gè)方法志鞍,

DefaultAnnotationEngine.forAnnotation(),


在這個(gè)方法中對(duì)annotationd的類型與已有的注解處理器對(duì)象集合進(jìn)行判斷是否包含,如果包含取出對(duì)應(yīng)的處理器對(duì)象方仿,如果不包含空實(shí)現(xiàn)一個(gè)注解處理器實(shí)現(xiàn)process方法返回為null固棚。也就是說(shuō)在DefaultAnnotationEngine對(duì)象的實(shí)例中只處理特定的注解生成其mock代理對(duì)象。

這個(gè)注解處理器的集合是在4中創(chuàng)建了deletage時(shí)創(chuàng)建了DefaultAnnotationEngine對(duì)象仙蚜,然后在其構(gòu)造方法中調(diào)用了注冊(cè)注解驅(qū)動(dòng)方法

private final Map, FieldAnnotationProcessor?> annotationProcessorMap = new HashMap, FieldAnnotationProcessor>();

? ? public DefaultAnnotationEngine() {

? ? ? ? registerAnnotationProcessor(Mock.class, new MockAnnotationProcessor());

? ? ? ? registerAnnotationProcessor(MockitoAnnotations.Mock.class, new MockitoAnnotationsMockAnnotationProcessor());

? ? ? ? registerAnnotationProcessor(Captor.class, new CaptorAnnotationProcessor());

? ? }


private void registerAnnotationProcessor(Class annotationClass, FieldAnnotationProcessor fieldAnnotationProcessor) {

? ? ? ? annotationProcessorMap.put(annotationClass, fieldAnnotationProcessor);

? ? }

DefaultAnnotationEngine.process()

根據(jù)獲取的annotationProcess對(duì)象執(zhí)行process方法此洲,如果不是從map中獲取的那么返回值就是null,在本測(cè)試中就是@mock的方法返回了MockAnnotationProcessor類型的注解驅(qū)動(dòng)委粉,


Mockito.mock(field.getType(), mockSettings);這個(gè)就是最后創(chuàng)建mock的cglib代理對(duì)象的方法呜师,對(duì)這個(gè)方法暫時(shí)就不繼續(xù)追蹤了,

小結(jié)

現(xiàn)在我們已經(jīng)將一個(gè)@Mock注解下的測(cè)試類中的field建立好了贾节,讓我們回到5的process方法中汁汗,能看見(jiàn)這個(gè)步驟就是重復(fù)的執(zhí)行這段邏輯:

獲取field的所有注解衷畦,調(diào)用createMockFor方法,然后在此方法中和DefaultAnnotationEngine預(yù)置的FieldAnnotationProcessor 實(shí)現(xiàn)類型集合做匹配知牌,滿足的獲取指定的注解處理器創(chuàng)建對(duì)應(yīng)的mock對(duì)象祈争。不滿足的創(chuàng)建一個(gè)匿名子類,其中實(shí)現(xiàn)的方法指定返回null角寸。以此將@Mock等注解和其他注解區(qū)分開(kāi)來(lái)菩混,只創(chuàng)建@Mock和@Captor等獨(dú)立的注解。如此步驟processIndependentAnnotations.process()就完成了扁藕。

spyAnnotationEngine.process的執(zhí)行類似沮峡,但是這個(gè)注解處理類是使用反射去根據(jù)類型創(chuàng)建一個(gè)真實(shí)的實(shí)例對(duì)象返回而不是創(chuàng)建一個(gè)mock的cglib對(duì)象。

7,InjectingAnnotationEngine .processInjectMocks

現(xiàn)在我們完成了processIndependentAnnotations纹磺,來(lái)看看現(xiàn)在的測(cè)試用例instance


可以看到現(xiàn)在a帖烘,b使用@Mock注解的field已經(jīng)存在cglib的代理對(duì)象了,使用@InjectMocks的doMainObject暫時(shí)還是null橄杨,現(xiàn)在來(lái)看processInjectMocks


方法內(nèi)部的核心方法是injectMocks秘症,內(nèi)部的邏輯從子類到父類最后到Object處理每個(gè)繼承層級(jí)的@InjectMocks注解

8 .InjectingAnnotationEngine.injectMocks

方法內(nèi)處理

1獲取測(cè)試用例的Class類型

2創(chuàng)建一個(gè)Field對(duì)象的set集合

3循環(huán)處理

4將class中所有InjectMocks注解的field放到mockDependentFields集合中

5將創(chuàng)建的mock對(duì)象添加到mocks集合中

6Class類型是Object跳出循環(huán)

7創(chuàng)建@InjectMock注解修飾的field

9DefaultInjectionEngine.injectMocksOnFields

這是根據(jù)依賴創(chuàng)建目標(biāo)測(cè)試類的mock對(duì)象。

最后方法完成的時(shí)候


可以看到目標(biāo)測(cè)試類創(chuàng)建完成式矫,依賴a乡摹,b也已經(jīng)注入。

3采转,總結(jié)

沒(méi)有去追蹤Junit和cglib只是將中間mock的注解的過(guò)程進(jìn)行了追蹤:

基本就是先創(chuàng)建mock對(duì)象聪廉,然后將根據(jù)依賴創(chuàng)建@InjectMocks的目標(biāo)測(cè)試類對(duì)象

其中注解的區(qū)分

1@InjectMocks和其他類型不同,

2@Spy和@Mock,@Captor等注解不同

4故慈,注意

在使用的過(guò)程中Rule一定要是pulic修飾的

在使用mockito的時(shí)候還出現(xiàn)了mock的對(duì)象在測(cè)試的時(shí)候報(bào)空指針的問(wèn)題板熊,我追蹤后發(fā)現(xiàn)是同類型的interface在注入@InjectMocks修飾的主目標(biāo)對(duì)象的時(shí)候是有排序的,會(huì)根據(jù)測(cè)試類中的filedName進(jìn)行排序依次向下注入察绷,解決辦法就是把@InjectMocks的所有field都進(jìn)行mock干签。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拆撼,隨后出現(xiàn)的幾起案子容劳,更是在濱河造成了極大的恐慌,老刑警劉巖闸度,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件竭贩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡莺禁,警方通過(guò)查閱死者的電腦和手機(jī)留量,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人肪获,你說(shuō)我怎么就攤上這事寝凌。” “怎么了孝赫?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵较木,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我青柄,道長(zhǎng)伐债,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任致开,我火速辦了婚禮峰锁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘双戳。我一直安慰自己虹蒋,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開(kāi)白布飒货。 她就那樣靜靜地躺著魄衅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塘辅。 梳的紋絲不亂的頭發(fā)上晃虫,一...
    開(kāi)封第一講書(shū)人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音扣墩,去河邊找鬼哲银。 笑死,一個(gè)胖子當(dāng)著我的面吹牛呻惕,可吹牛的內(nèi)容都是我干的荆责。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼亚脆,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼草巡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起型酥,我...
    開(kāi)封第一講書(shū)人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎查乒,沒(méi)想到半個(gè)月后弥喉,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡玛迄,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年由境,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虏杰,死狀恐怖讥蟆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情纺阔,我是刑警寧澤瘸彤,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站笛钝,受9級(jí)特大地震影響质况,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜玻靡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一结榄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧囤捻,春花似錦臼朗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瘟则,卻和暖如春黎炉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背醋拧。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工慷嗜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人丹壕。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓庆械,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親菌赖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缭乘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)琉用,斷路器堕绩,智...
    卡卡羅2017閱讀 134,652評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,085評(píng)論 25 707
  • 木工坊給孩子們開(kāi)辟了一個(gè)全新的集游戲、手工操作為一體的專業(yè)活動(dòng)場(chǎng)所邑时。我們從認(rèn)識(shí)工具奴紧、講解工具的使用方法開(kāi)...
    四葉草掃地僧閱讀 1,097評(píng)論 0 2
  • 戒煙對(duì)于身體的健康是必須的。但為什么我們的身邊總有人戒不了煙呢晶丘? 有一種說(shuō)法黍氮,說(shuō)是身體的細(xì)胞對(duì)煙草燃燒后產(chǎn)生的有害...
    舒云萱閱讀 1,221評(píng)論 1 1
  • Part1. “不要什么都管著我了唐含!我已經(jīng)長(zhǎng)大了!不是過(guò)去的小女孩了沫浆!”繼近幾個(gè)月來(lái)不停的小摩擦和小爭(zhēng)吵過(guò)后捷枯,十七...
    math_soccer閱讀 278評(píng)論 0 1