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干签。