<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    少年阿賓

    那些青春的歲月

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

    1 現(xiàn)有的單元測試框架
           單元測試是保證程序正確性的一種有效的測試手段,對(duì)于不同的開發(fā)語言,通常都能找到相應(yīng)的單元框架。



           借助于這些單測框架的幫助,能夠使得我們編寫單元測試用例的過程變得便捷而優(yōu)雅。框架幫我們提供了case的管理,執(zhí)行,斷言集,運(yùn)行參數(shù),全局事件工作,所有的這些使得我們只需關(guān)注:于對(duì)于特定的輸入,被測對(duì)象的返回是否正常。
           那么,這些xUnit系列的單元測試框架是如何做到這些的了?分析這些框架,發(fā)現(xiàn)所有的單元測試框架都是基于以下的一種體系結(jié)構(gòu)設(shè)計(jì)的。

     
           如上圖所示,單測框架中通常包括TestRunner, Test, TestResult, TestCase, TestSuite, TestFixture六個(gè)組件。
    TestRuner:負(fù)責(zé)驅(qū)動(dòng)單元測試用例的執(zhí)行,匯報(bào)測試執(zhí)行的結(jié)果,從而簡化測試
    TestFixture:以測試套件的形式提供setUp()和tearDown()方法,保證兩個(gè)test case之間的執(zhí)行是相互獨(dú)立,互不影響的。
    TestResult:這個(gè)組件用于收集每個(gè)test case的執(zhí)行結(jié)果
    Test:作為TestSuite和TestCase的父類暴露run()方法為TestRunner調(diào)用
    TestCase:暴露給用戶的一個(gè)類,用戶通過繼承TestCase,編寫自己的測試用例邏輯
    TestSuite:提供suite功能管理testCase
           正因?yàn)橄嗨频捏w系結(jié)構(gòu),所以大多數(shù)單元測試框架都提供了類似的功能和使用方法。那么在單測中引入單元測試框架會(huì)帶來什么好處,在現(xiàn)有單元測試框架下還會(huì)存在什么樣不能解決的問題呢?
    2 單元測試框架的優(yōu)點(diǎn)與一些問題
           在單元測試中引入單測框架使得編寫單測用例時(shí),不需要再關(guān)注于如何驅(qū)動(dòng)case的執(zhí)行,如何收集結(jié)果,如何管理case集,只需要關(guān)注于如何寫好單個(gè)測試用例即可;同時(shí),在一些測試框架中通過提供豐富的斷言集,公用方法,以及運(yùn)行參數(shù)使得編寫單個(gè)testcase的過程得到了最大的簡化。
           那這其中會(huì)存在什么樣的疑問了?
           我在單元測試框架中寫一個(gè)TestCase,與我單獨(dú)寫一個(gè)cpp文件在main()方法里寫測試代碼有什么本質(zhì)卻別嗎?用了單元測試框架,并沒有解決我在對(duì)復(fù)雜系統(tǒng)做單測時(shí)遇到的問題。
           沒錯(cuò),對(duì)于單個(gè)case這兩者從本質(zhì)上說是沒有區(qū)別的。單元測試框架本身并沒有告訴你如何去寫TestCase,在這一點(diǎn)上他是沒有提供任何幫助的。所以對(duì)于一些復(fù)雜的場景,只用單元測試框架是有點(diǎn)多少顯得無能為力的。
           使用單元測試框架往往適用于以下場景的測試:單個(gè)函數(shù),一個(gè)class,或者幾個(gè)功能相關(guān)class的測試,對(duì)于純函數(shù)測試,接口級(jí)別的測試尤其適用,如房貸計(jì)算器公式的測試。
           但是,對(duì)于一些復(fù)雜場景:
    ? 被測對(duì)象依賴復(fù)雜,甚至無法簡單new出這個(gè)對(duì)象
    ? 對(duì)于一些failure場景的測試
    ? 被測對(duì)象中涉及多線程合作
    ? 被測對(duì)象通過消息與外界交互的場景
    ? …
           單純依賴單測框架是無法實(shí)現(xiàn)單元測試的,而從某種意義上來說,這些場景反而是測試中的重點(diǎn)。
           以分布式系統(tǒng)的測試為例,class 與 function級(jí)別的單元測試對(duì)整個(gè)系統(tǒng)的幫助不大,當(dāng)然,這種單元測試對(duì)單個(gè)程序的質(zhì)量有幫助;分布式系統(tǒng)測試的要點(diǎn)是測試進(jìn)程間的交互:一個(gè)進(jìn)程收到客戶請(qǐng)求,該如何處理,然后轉(zhuǎn)發(fā)給其他進(jìn)程;收到響應(yīng)之后,又修改并應(yīng)答客戶;同時(shí)分布式系統(tǒng)測試中通常更關(guān)注一些異常路徑的測試,這些場景才是測試中的重點(diǎn),也是難點(diǎn)所在。
           Mock方法的引入通常能幫助我們解決以上場景中遇到的難題。
    3 Mock的引入帶來了什么
           在維基百科上這樣描述Mock:In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior. of a human in vehicle impacts.
           Mock通常是指,在測試一個(gè)對(duì)象A時(shí),我們構(gòu)造一些假的對(duì)象來模擬與A之間的交互,而這些Mock對(duì)象的行為是我們事先設(shè)定且符合預(yù)期。通過這些Mock對(duì)象來測試A在正常邏輯,異常邏輯或壓力情況下工作是否正常。
           引入Mock最大的優(yōu)勢在于:Mock的行為固定,它確保當(dāng)你訪問該Mock的某個(gè)方法時(shí)總是能夠獲得一個(gè)沒有任何邏輯的直接就返回的預(yù)期結(jié)果。
           Mock Object的使用通常會(huì)帶來以下一些好處:
    ? 隔絕其他模塊出錯(cuò)引起本模塊的測試錯(cuò)誤。
    ? 隔絕其他模塊的開發(fā)狀態(tài),只要定義好接口,不用管他們開發(fā)有沒有完成。
    ? 一些速度較慢的操作,可以用Mock Object代替,快速返回。
           對(duì)于分布式系統(tǒng)的測試,使用Mock Object會(huì)有另外兩項(xiàng)很重要的收益:
    ? 通過Mock Object可以將一些分布式測試轉(zhuǎn)化為本地的測試
    ? 將Mock用于壓力測試,可以解決測試集群無法模擬線上集群大規(guī)模下的壓力
    4 Mock的應(yīng)用場景
           在使用Mock的過程中,發(fā)現(xiàn)Mock是有一些通用性的,對(duì)于一些應(yīng)用場景,是非常適合使用Mock的:
    ? 真實(shí)對(duì)象具有不可確定的行為(產(chǎn)生不可預(yù)測的結(jié)果,如股票的行情)
    ? 真實(shí)對(duì)象很難被創(chuàng)建(比如具體的web容器)
    ? 真實(shí)對(duì)象的某些行為很難觸發(fā)(比如網(wǎng)絡(luò)錯(cuò)誤)
    ? 真實(shí)情況令程序的運(yùn)行速度很慢
    ? 真實(shí)對(duì)象有用戶界面
    ? 測試需要詢問真實(shí)對(duì)象它是如何被調(diào)用的(比如測試可能需要驗(yàn)證某個(gè)回調(diào)函數(shù)是否被調(diào)用了)
    ? 真實(shí)對(duì)象實(shí)際上并不存在(當(dāng)需要和其他開發(fā)小組,或者新的硬件系統(tǒng)打交道的時(shí)候,這是一個(gè)普遍的問題)
           當(dāng)然,也有一些不得不Mock的場景:
    一些比較難構(gòu)造的Object:這類Object通常有很多依賴,在單元測試中構(gòu)造出這樣類通常花費(fèi)的成本太大。
    ? 執(zhí)行操作的時(shí)間較長Object:有一些Object的操作費(fèi)時(shí),而被測對(duì)象依賴于這一個(gè)操作的執(zhí)行結(jié)果,例如大文件寫操作,數(shù)據(jù)的更新等等,出于測試的需求,通常將這類操作進(jìn)行Mock。
    ? 異常邏輯:一些異常的邏輯往往在正常測試中是很難觸發(fā)的,通過Mock可以人為的控制觸發(fā)異常邏輯。
            在一些壓力測試的場景下,也不得不使用Mock,例如在分布式系統(tǒng)測試中,通常需要測試一些單點(diǎn)(如namenode,jobtracker)在壓力場景下的工作是否正常。而通常測試集群在正常邏輯下無法提供足夠的壓力(主要原因是受限于機(jī)器數(shù)量),這時(shí)候就需要應(yīng)用Mock去滿足。
            在這些場景下,我們應(yīng)該如何去做Mock的工作了,一些現(xiàn)有的Mock工具可以幫助我們進(jìn)行Mock工作。
    5 Mock工具的介紹
           手動(dòng)的構(gòu)造 Mock 對(duì)象通常帶來額外的編碼量,而且這些為創(chuàng)建 Mock 對(duì)象而編寫的代碼很有可能引入錯(cuò)誤。目前,有許多開源項(xiàng)目對(duì)動(dòng)態(tài)構(gòu)建 Mock 對(duì)象提供了支持,這些項(xiàng)目能夠根據(jù)現(xiàn)有的接口或類動(dòng)態(tài)生成,這樣不僅能避免額外的編碼工作,同時(shí)也降低了引入錯(cuò)誤的可能。
    C++:   GoogleMock   http://code.google.com/p/googlemock/

    Java:   EasyMock   http://easymock.org/

           通常Mock工具通過簡單的方法對(duì)于給定的接口生成 Mock 對(duì)象的類庫。它提供對(duì)接口的模擬,能夠通過錄制、回放、檢查三步來完成大體的測試過程,可以驗(yàn)證方法的調(diào)用種類、次數(shù)、順序,可以令 Mock 對(duì)象返回指定的值或拋出指定異常。通過這些Mock工具我們可以方便的構(gòu)造 Mock 對(duì)象從而使單元測試順利進(jìn)行,能夠應(yīng)用于更加復(fù)雜的測試場景。
           以EasyMock為例,通過 EasyMock,我們可以為指定的接口動(dòng)態(tài)的創(chuàng)建 Mock 對(duì)象,并利用 Mock 對(duì)象來模擬協(xié)同模塊,從而使單元測試順利進(jìn)行。這個(gè)過程大致可以劃分為以下幾個(gè)步驟:
    使用 EasyMock 生成 Mock 對(duì)象
    ? 設(shè)定 Mock 對(duì)象的預(yù)期行為和輸出 
    ? 將 Mock 對(duì)象切換到 Replay 狀態(tài)
    ? 調(diào)用 Mock 對(duì)象方法進(jìn)行單元測試
    ? 對(duì) Mock 對(duì)象的行為進(jìn)行驗(yàn)證
    EasyMock的使用和原理:  http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

           EasyMock 后臺(tái)處理的主要原理是利用 java.lang.reflect.Proxy 為指定的接口創(chuàng)建一個(gè)動(dòng)態(tài)代理,這個(gè)動(dòng)態(tài)代理,就是我們?cè)诰幋a中用到的 Mock 對(duì)象。EasyMock 還為這個(gè)動(dòng)態(tài)代理提供了一個(gè) InvocationHandler 接口的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)類的主要功能就是將動(dòng)態(tài)代理的預(yù)期行為記錄在某個(gè)映射表中和在實(shí)際調(diào)用時(shí)從這個(gè)映射表中取出預(yù)期輸出。
           借助類似于EasyMock這樣工具,大大降低了編寫Mock對(duì)象的成本,通常來說Mock工具依賴于單元測試框架,為用戶編寫TestCase提供便利,但是本身依賴于單元測試框架去驅(qū)動(dòng),管理case,以及收集測試結(jié)果。例如EasyMock依賴于JUint,GoogleMock依賴于Gtest。
           那么有了單元測試框架和相應(yīng)的Mock工具就萬事俱備了,還有什么樣的問題?正如單元測試框架沒有告訴你如何寫TestCase一樣,Mock工具也沒有告訴你如何去選擇Mock的點(diǎn)。
    6 如何選擇恰當(dāng)?shù)膍ock點(diǎn)
           對(duì)于Mock這里存在兩個(gè)誤區(qū),1.是Mock的對(duì)象越多越好;2.Mock會(huì)引入巨大的工作量,通常得不償失。這都是源于不恰當(dāng)?shù)腗ock點(diǎn)的選取。
           這里說的如何選擇恰當(dāng)?shù)膍ock點(diǎn),是說對(duì)于一個(gè)被測對(duì)象,我們應(yīng)當(dāng)在外圍選擇恰當(dāng)?shù)膍ock對(duì)象,以及需要mock的接口。因?yàn)閷?duì)于任意一個(gè)對(duì)象,任意一段代碼邏輯我們都是有辦法進(jìn)行Mock的,而Mock點(diǎn)選擇直接決定了我們Mock的工作量以及測試效果。從另外一種意義上來說,不恰當(dāng)Mock選擇反而會(huì)對(duì)我們的測試產(chǎn)生誤導(dǎo),從而在后期的集成和系統(tǒng)測試中引入更多的問題。
           在mock點(diǎn)的選擇過程中,以下的一些點(diǎn)會(huì)是一些不錯(cuò)的選擇
    ? 網(wǎng)絡(luò)交互:如果兩個(gè)被測模塊之間是通過網(wǎng)絡(luò)進(jìn)行交互的,那么對(duì)于網(wǎng)絡(luò)交互進(jìn)行Mock通常是比較合適的,如RPC
    ? 外部資源:比如文件系統(tǒng)、數(shù)據(jù)源,如果被測對(duì)象對(duì)此類外部資源依賴性非常強(qiáng),而其行為的不可預(yù)測性很可能導(dǎo)致測試的隨機(jī)失敗,此類的外部資源也適合進(jìn)行Mock。
    ? UI:因?yàn)閁I很多時(shí)候都是用戶行為觸發(fā)事件,系統(tǒng)本身只是對(duì)這些觸發(fā)事件進(jìn)行相應(yīng),對(duì)這類UI做Mock,往往能夠?qū)崿F(xiàn)很好的收益,很多基于關(guān)鍵字驅(qū)動(dòng)的框架都是基于UI進(jìn)行Mock的
    ? 第三方API:當(dāng)接口屬于使用者,通過Mock該接口來確定測試使用者與接口的交互。
           當(dāng)然如何做Mock一定是與被系統(tǒng)的特性精密關(guān)聯(lián)的,一些強(qiáng)制性的約束和規(guī)范是不合適的。這里介紹幾個(gè)做的比較好的mock的例子。
           1. 殺毒軟件更新部署模塊的Mock
           這個(gè)例子源于一款殺毒產(chǎn)品的更新部署模塊的測試。對(duì)于一個(gè)殺毒軟件客戶端而言,需要通過更新檢查模塊與病毒庫Server進(jìn)行交互,如果發(fā)現(xiàn)病毒庫有更新則觸發(fā)病毒庫部署模塊的最新病毒庫的數(shù)據(jù)請(qǐng)求和部署工作,要求部署完成后殺毒軟件客戶端能夠正常工作。
     

            對(duì)于這一場景的測試,當(dāng)時(shí)受限于這樣一個(gè)條件,通常的病毒庫server通常最多一天只更新一次病毒庫,也就是說如果使用真實(shí)的病毒庫server,那么針對(duì)更新部署模塊的測試一天只能被觸發(fā)一次。這是測試中所不能容忍的,通過對(duì)病毒庫server進(jìn)行mock可以解決這個(gè)問題。
           對(duì)于這個(gè)場景可以采取這樣一種Mock方式:用一個(gè)本地文件夾來模擬病毒庫server,選擇更新部署模塊與病毒庫server之間交互的兩個(gè)函數(shù)checkVersion(),reqData()函數(shù)進(jìn)行Mock。
           checkVersion()工作原先的工作是檢查病毒庫Server的版本號(hào),以決定是否觸發(fā)更新,將其行為Mock為檢查一個(gè)本地文件夾中病毒庫的版本號(hào);reqData()原有的行為是從病毒庫Server拖取病毒庫文件,將其Mock為從本地文件夾中拖取病毒庫文件。通過這種方式我們用一個(gè)本地文件夾Mock病毒庫Server的行為,其帶來的產(chǎn)出是:我們可以隨意的觸發(fā)病毒庫更新操作以及各種異常。通過這種方式發(fā)現(xiàn)了一個(gè)在更新部署過程中,病毒庫Server的病毒庫版本發(fā)生改變?cè)斐沙鲥e(cuò)的嚴(yán)重bug,這個(gè)是在原有一天才觸發(fā)一次更新操作的情況下永遠(yuǎn)也無法發(fā)現(xiàn)的。
           2. 分布式系統(tǒng)中對(duì)NameNode模塊的測試
     

           在測試NameNode模塊的過程中存在這樣一個(gè)問題,在正常邏輯無壓力條件下NameNode模塊都是工作正常的。但是線上集群在大壓力的情況下,是有可能觸發(fā)NameNode的問題的。但是原有的測試方法下,我們是無法對(duì)NameNode模擬大壓力的場景的(因?yàn)镹ameNode的壓力主要來源于DateNode數(shù)量,而我們測試集群是遠(yuǎn)遠(yuǎn)無法達(dá)到線上幾千臺(tái)機(jī)器的規(guī)模的),而NameNode單點(diǎn)的性能瓶頸問題恰恰是測試的重點(diǎn),真實(shí)的DataNode是無法滿足測試需求的,我們必須對(duì)DataNode進(jìn)行Mock。
     

           如何對(duì)DateNode進(jìn)行Mock了,最直觀的想法是選擇NameNode與DataNode之間的交互接口進(jìn)行Mock,也就是他們之間的RPC交互,但是由于NameNode與DataNode之間的交互信息種類很多,所以其實(shí)這并不是一種很好的選擇。
           換個(gè)角度來想,NameNode之上的壓力是源于對(duì)HDFS的讀寫操作造成的NameNode上元數(shù)據(jù)的維護(hù),也就是說,對(duì)于NameNode而言,其實(shí)他并不關(guān)心數(shù)據(jù)到底寫到哪里去了,只關(guān)心數(shù)據(jù)是否讀寫成功。如果是這種場景Mock就可以變的簡單了,我們可以直接將DataNode上對(duì)塊的操作進(jìn)行mock,比如,對(duì)一次寫請(qǐng)求,DataNode并不觸發(fā)真實(shí)的寫操作,而直接返回成功。通過這種方式,DataNode去除了執(zhí)行功能,只保留了消息交互功能,間接的實(shí)現(xiàn)了我們的測試需求,且工作量比之第一種方案小很多。
           3. 開源社區(qū)提供的MRUnit測試框架
           在原有框架下,對(duì)于MapReduce程序的測試通常是無法在本地驗(yàn)證的,更不用說對(duì)MapReduce程序進(jìn)行單測了。而MRUnit通過一個(gè)簡單而優(yōu)雅的Mock,卻實(shí)現(xiàn)了一個(gè)基于MapReduce程序的單測框架。

    基于MRUINT框架可以將單測寫成如下形式:

     

           在這個(gè)框架中定義了MapDriver,ReducerDriver,MapReduceDriver三個(gè)有點(diǎn)類似容器的driver,通過driver來驅(qū)動(dòng)map,reduce或者整個(gè)mapreduce過程的執(zhí)行。
           如上例,在driver中設(shè)定mapper為IdentityMapper,通過withInput方法設(shè)定輸入數(shù)據(jù),通過withOutput方法設(shè)定預(yù)期結(jié)果,通過runTest方法來觸發(fā)執(zhí)行并進(jìn)行結(jié)果檢測
           他的實(shí)現(xiàn)原理是將outputCollector做Mock,outputCollectort中的emit方法實(shí)現(xiàn)的邏輯是將數(shù)據(jù)寫到文件系統(tǒng)中,Mock后是通過另外一個(gè)進(jìn)程去收集數(shù)據(jù)并保存在內(nèi)存中,從而實(shí)現(xiàn)最終結(jié)果的可檢驗(yàn)(在自己的數(shù)據(jù)結(jié)構(gòu)中比對(duì)結(jié)果)。
           實(shí)現(xiàn)的原理很簡單,這樣做mock就會(huì)精巧,只選擇最底層的一些簡單卻又依賴廣泛的點(diǎn)(依賴廣泛指模塊間的數(shù)據(jù)流通常都走這樣的點(diǎn)過)做mock,這樣通常效果很好且簡單
           當(dāng)然這個(gè)例子中也有一些缺陷:1.因?yàn)樵趏utputcollector層做mock的數(shù)據(jù)截取,使得無法過partition的分桶邏輯;2.這個(gè)框架是寫內(nèi)存的,無法最終改成壓力性能測試工具。

    7 附錄
    1. EasyMock示例:
     

    posted on 2013-04-20 15:21 abin 閱讀(419) 評(píng)論(0)  編輯  收藏 所屬分類: easyMock
    主站蜘蛛池模板: 久久精品亚洲综合一品| 免费无码精品黄AV电影| 亚洲熟妇少妇任你躁在线观看无码| 亚洲欧洲日产国产最新| 亚洲国产精品免费视频| 亚洲国产精品无码久久一区二区| jizz免费观看| 亚洲精品久久久www| 国产成人精品免费视频大全| 亚洲精品国产精品乱码不卡| 成人免费夜片在线观看| a级亚洲片精品久久久久久久| www成人免费视频| 亚洲精品国产精品乱码视色| 中文字幕在线视频免费观看| 日韩亚洲人成在线综合日本| 特级精品毛片免费观看| 91亚洲自偷手机在线观看| 四虎免费影院ww4164h| 亚洲男人天堂2018av| 真人做人试看60分钟免费视频| 亚洲国产精品婷婷久久| 亚欧色视频在线观看免费| 久久亚洲国产最新网站| 四虎影视精品永久免费| 亚洲精品偷拍视频免费观看| 亚洲AV无码乱码在线观看富二代| 久久这里只精品热免费99| 亚洲av乱码一区二区三区香蕉 | 美女视频黄频a免费| 国产亚洲精品自在线观看| 国产白丝无码免费视频| 国产精品亚洲自在线播放页码| 国产大片免费观看中文字幕| 韩国免费a级作爱片无码| 亚洲视频一区二区在线观看| 黄a大片av永久免费| 最新亚洲成av人免费看| 免费H网站在线观看的| 精品无码专区亚洲| 亚洲欧洲美洲无码精品VA|