mock單元測試 mockito實踐

1基本的使用步驟

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

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

@Rule

public MokitoRule rule = MockitoJUnit.rule();

注意這里的修飾符public,如果沒有這個修飾符的話使用mock測試會報錯

?@Rule

?public ExpectedException thrown = ExpectedException.none();

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

3,對目標測試類加@InjectMocks注解

4,對目標測試類的依賴屬性使用@mock創(chuàng)建依賴

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

2mock對象創(chuàng)建和目標測試類的依賴注入

這里我還沒有看juint執(zhí)行的邏輯,只是看了mock環(huán)境下獲取注解創(chuàng)建mock對象,并將mock的對向注入到@Injectmocks的目標測試對象中去的邏輯。

1,測試前的準備

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

創(chuàng)建一個interface用于目標測試類的filed的type。

public interface MyInterface { public void show(); }

創(chuàng)建一個目標測試類,其中聲明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)建一個測試用例對象用于測試目標測試類


public class MyMockTest {

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

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

@InjectMocks private DoMainObject doMainObject;//目標測試類

@Mock private MyInterface a;//目標測試類的field a

@Mock private MyInterface b;//目標測試類的filed b

@Test public void testShow(){

doMainObject.aShow();

doMainObject.bShow();

}

}

2進行debug測試

1測試類中加斷點debug

我從rule對象執(zhí)行的時候開始追蹤,junit的運行原理略過


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


其中的testClass就是測試用例,MyMockTest的實例,annotationEngine是默認的注解驅(qū)動InjectingAnnotationEngine,

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

3, InjectingAnnotationEngine .process方法


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

processIndependentAnnotations處理獨立的filed,其實就是測試用例中有@mock注解的filed,這里就是a和b。processInjectMocks處理依賴于獨立mock對象的filed,就是測試用例中有@InjectMocks注解的filed,依賴于mock對象的目標測試類,這里就是DoMainObject,先看processInjectMocks

4, processIndependentAnnotations


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

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

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

3獲取測試用例的父類,賦值給原來的變量

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

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

5delegate.process


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

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

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

6, DefaultAnnotationEngine.?createMockFor

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


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

DefaultAnnotationEngine.forAnnotation(),


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

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

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對象執(zhí)行process方法,如果不是從map中獲取的那么返回值就是null,在本測試中就是@mock的方法返回了MockAnnotationProcessor類型的注解驅(qū)動,


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

小結(jié)

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

獲取field的所有注解,調(diào)用createMockFor方法,然后在此方法中和DefaultAnnotationEngine預(yù)置的FieldAnnotationProcessor 實現(xiàn)類型集合做匹配,滿足的獲取指定的注解處理器創(chuàng)建對應(yīng)的mock對象。不滿足的創(chuàng)建一個匿名子類,其中實現(xiàn)的方法指定返回null。以此將@Mock等注解和其他注解區(qū)分開來,只創(chuàng)建@Mock和@Captor等獨立的注解。如此步驟processIndependentAnnotations.process()就完成了。

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

7,InjectingAnnotationEngine .processInjectMocks

現(xiàn)在我們完成了processIndependentAnnotations,來看看現(xiàn)在的測試用例instance


可以看到現(xiàn)在a,b使用@Mock注解的field已經(jīng)存在cglib的代理對象了,使用@InjectMocks的doMainObject暫時還是null,現(xiàn)在來看processInjectMocks


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

8 .InjectingAnnotationEngine.injectMocks

方法內(nèi)處理

1獲取測試用例的Class類型

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

3循環(huán)處理

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

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

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

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

9DefaultInjectionEngine.injectMocksOnFields

這是根據(jù)依賴創(chuàng)建目標測試類的mock對象。

最后方法完成的時候


可以看到目標測試類創(chuàng)建完成,依賴a,b也已經(jīng)注入。

3,總結(jié)

沒有去追蹤Junit和cglib只是將中間mock的注解的過程進行了追蹤:

基本就是先創(chuàng)建mock對象,然后將根據(jù)依賴創(chuàng)建@InjectMocks的目標測試類對象

其中注解的區(qū)分

1@InjectMocks和其他類型不同,

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

4,注意

在使用的過程中Rule一定要是pulic修飾的

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,612評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,146評論 25 708
  • 木工坊給孩子們開辟了一個全新的集游戲、手工操作為一體的專業(yè)活動場所。我們從認識工具、講解工具的使用方法開...
    四葉草掃地僧閱讀 1,254評論 0 2
  • 戒煙對于身體的健康是必須的。但為什么我們的身邊總有人戒不了煙呢? 有一種說法,說是身體的細胞對煙草燃燒后產(chǎn)生的有害...
    舒云萱閱讀 1,273評論 1 1
  • Part1. “不要什么都管著我了!我已經(jīng)長大了!不是過去的小女孩了!”繼近幾個月來不停的小摩擦和小爭吵過后,十七...
    math_soccer閱讀 323評論 0 1

友情鏈接更多精彩內(nèi)容