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

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

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

    qileilove

    blog已經轉移至github,大家請訪問 http://qaseven.github.io/

     第二章 單元測試的基本概念和核心技法

      第二章 單元測試的基本概念和核心技法

      2.1 良好的單元測試——定義

      我們已經了解了程序員需要單元測試,下面我們來給單元測試作一個完整的定義:

      ● 定義: 單元測試是一段自動執行的代碼,它調用被測類或被測方法,然后驗證關于被測類或被測方法邏輯行為的假設確實成立。單元測試幾乎總是用單元測試框架(unit testing framework)來寫就的,單元測試是易于寫就、執行快速、完全自動化、值得依賴、易于閱讀并易于維護的。

      這個定義有點長,但是它卻包含了大量重要信息:

      ● 單元測試的測試重點是被測類或被測方法的邏輯行為。所謂“邏輯行為”,指的是含有諸如判斷、循環、選擇、計算或其他決策過程的代碼。

      ● 單元測試框架是輔助程序員寫就單元測試的利器。

      ● 單元測試的代碼本身,同被測代碼一樣,也應該是值得依賴、易于閱讀并易于維護的。

      有了單元測試的定義,我們來看看有關單元測試的幾個基本概念,這些基本概念會在后面的章節中反復出現。

      ● 被測類(Class Under Test)和被測方法(Method Under Test):顧名思義,就是測試代碼所操練的類或方法。

      ● 測試類(Test Fixture)和測試方法(Test Method):負責操練被測類和被測方法。Test Method一般都是Test Fixture的成員函數。

      ● 測試運行器(Test Runner):負責自動執行測試類中的測試方法。Test Runner可以是命令行界面的,也可以是GUI的。

      2.2 進行單元測試的核心技術和核心手法

      2.2.1 核心技術

      前面已經講到,單元測試所針對的目標是“單個”類或“單個”方法(這里的“方法”指C++中的自由函數)。這表明我們在單元測試中要做的主要任務是創建出被測類的對象,并調用該對象的被測方法。但是我們都知道,一個類幾乎不可能完全不依賴于其他類。這種類之間的依賴會導致我們無法順利地將一個被測類納入單元測試覆蓋這下,因為單元測試需要的是對被測類這一個類的測試,而不是同時測試被測類和它的合作者類。

      因此,我們在把ClassUnderTest納入單元測試時,也就必須先把ClassUnderTest與CollaboratorClass之間的依賴“打破”,這個過程稱為“解依賴”(dependency-breaking)。解依賴就是進行單元測試的核心技術。解依賴的目標是希望能把不可控的CollaboratorClass替換成由我們控制的偽合作者類(FakeCollaboratorClass),并使被測類能方便地選擇是依賴于真實的合作者類還是偽合作者類。對于產品代碼,被測類依賴的是真實的合作者類,而在單元測試中,被測類依賴的是由我們控制的偽合作者。

      在單元測試代碼中使用可控的FakeCollaboratorClass,這給我們帶來了兩個便利:

      ● 我們可以通過FakeCollaboratorClass向ClassUnderTest返回任何我們指定的結果。

      ● 我們可以通過FakeCollaboratorClass來感知ClassUnderTest所做的動作。

      這實際上就是FakeCollaboratorClass的兩種表現形式:

      ● Stub:用于向ClassUnderTest返回我們指定的結果。

      ● Mock:用于感知ClassUnderTest的行為。

      我們明白了“解依賴”是單元測試的核心技術。那么具體怎樣實現解依賴呢?下面我們就來介紹4種相關的核心手法,其中前2種與CollaboratorClass有關,后2種與ClassUnderTest有關,這4種手法對于解決絕大多數的解依賴問題都適用。

     2.2.2 “接口提取”和“Virtual and Override”

      “接口提取”手法是對CollaboratorClass提取出一個抽象接口CollaboratorService,然后讓CollaboratorClass和FakeCollaboratorClass都去實現這個接口,而ClassUnderTest則由直接依賴CollaboratorClass轉向依賴于抽象接口CollaboratorService, 如下圖所示。

      實際上,“接口提取”手法是一種非常好的手法,它使得我們的代碼遵循“依賴抽象原則”,遵循這個原則的軟件具有較好的靈活性,這是具有可測試性的軟件也具有較好的設計的一個佐證。

      而“Virtual and Override”手法則是使CollaboratorClass中的被依賴方法成為virtual,然后讓FakeCollaboratorClass去公有繼承CollaboratorClass,并且override那些虛函數,從而替換掉 CollaboratorClass中的行為。這種手法如下圖所示。

      總體上講,我們推薦優先使用“接口提取”手法,因為這將使代碼遵循“依賴抽象原則”,從而使軟件更好地應對今后的變化。但是,“Virtual and Override”方法也是有其一席之地的,我們后面會看到例子。

      2.2.3 “參數注入”和“Extract and Override”

      在ClassUnderTest這邊,對CollaboratorClass的依賴的產生方式也可以劃分成兩類:

      依賴是通過方法參數傳入的,這種形式的依賴被稱為“參數注入”式依賴(parameter injection dependency)。參數注入式依賴是一種耦合度較低的依賴產生形式,因此對ClassUnderTest的影響不大,一般最多只需要把方法的簽名由直接依賴CollaboratorClass改成依賴接口CollaboratorService。

      依賴是在被測方法的方法體內部產生的,這種依賴被稱為“隱藏式”依賴(hidden dependency)。隱藏式依賴有多種表現形式:

      ● 直接創建CollaboratorClass對象作為局部變量或成員變量。

      ● 通過一個工廠方法來產生CollaboratorClass對象。

      ● 通過一個工廠類來產生CollaboratorClass對象。

      隱藏式依賴是一種耦合程度較高的依賴,因此是我們著重需要“打破”的依賴。一種方法是把隱藏式依賴轉變成參數注入式依賴,我們將在后面的小節中看到這種方法的應用。而另一種方法,則是使用“Extract and Override”手法,即:我們給ClassUnderTest引入一個virtual的工廠成員函數,來返回一個CollaboratorClass對象的引用,然后對ClassUnderTest派生出一個子類,在該子類中override這個工廠成員函數,讓它返回一個FakeCollaboratorClass對象的引用,如下圖所示。

      這里的TestingClassUnderTest被稱為“測試用子類”(Testing Subclass)。這時,在Test Fixture中被實例化的其實就是測試用子類,而不是被測類本身。

      以上我們研究了進行單元測試所需要的核心技術,以及4種最常用的核心手法,這些手法足以應付絕大多數的情況,但是,仍然有一些特殊情況需要我們特別注意,我們從下一節開始,以Q&A的形式,討論這些特殊情況。

     2.3 應付構造函數中的隱藏式依賴

      當ClassUnderTest中出現隱藏式依賴時,最常用的有兩種手法來打破這種依賴。我們分別來看一下。

      2.3.1 轉變成參數注入式依賴

      對于像C#和Java這樣的語言,由于它們支持在一個構造函數中去調用另一個構造函數,因此可以很方便地增加一個構造函數,把隱藏式依賴轉變成參數注入式依賴。下面的UML圖闡釋了這種手法。

      而對于C++,由于它沒有提供在構造函數中調用另一個構造函數的功能,因此通常的作法就是把公共的的初始化代碼放入一個init()私有方法中去,讓不同的構造函數去調用init()方法。

      2.3.2 使用“調包方法”

      還可以考慮給ClassUndertTest引入一個“調包方法”,使得測試類和測試方法可以很方便地將合作者類“調包”成偽合作者類。這里的“調包方法”本質上就是一個setter方法,但是為了表明這個特殊的setter方法只應該被測試類和測試方法調用,我們一般給調包方法命名為SupersedeCollaborator()。下圖就是一個演示。

      這里必須要提醒的是,在C++中使用這個手法時必須注意在“調包方法”中不能引起資源泄漏,也就是說,必須先釋放掉原有的合作者對象,再以偽合作者替代之。

      2.4 怎樣測試ClassUnderTest中的private方法?

      如果要測試一個類的private方法,答案的總體思路是:適當打破訪問權限。也就是說,我們可以把private方法聲明成protected方法,然后對ClassUnderTest進行派生,得到一個“測試用子類”,在該“測試用子類”中聲明一個public方法,該方法是ClassUnderTest中的protected方法的代理。這種手法可以用下圖來表示。


     2.4 應付“全局依賴”

      所謂“全局依賴”,是指被測類或被測方法所依賴的合作者是一個“全局單件”,包括C#/Java/C++中的“單件類”(Singleton)和C++中的全局變量和自由方法(實際上, Singleton可視為全局變量在面向對象語言中的變種)。我們下面來看看怎么應對這些情況。

      2.4.1 使用“封裝全局引用”手法來解除對全局變量和自由方法的依賴

      要打破對全局變量和自由方法的依賴,其實基本思想就是:把它們納入到一個類中,讓它們成為一個類的成員變量和成員方法。一旦把全局變量和自由函數封裝進了某個類,那么我們就可以繼續利用2.2.2節提到的兩種手法來引入偽合作者了。

      2.4.2 使用“靜態調包方法”來解除對單件類的依賴

      單件類往往具有3個特點:

      (1)單件類的構造函數通常被設為private。

     ?。?)單件類具有一個靜態成員變量,該成員變量持有該類的唯一一個實例。

      (3)單件類具有一個靜態方法,用來提供對單件實例的訪問,通常該方法名叫getInstance()。

      由于單件類被設計成強制性地在整個程序中只有一份,因此要偽造它比較困難。我們推薦使用“靜態調包方法”手法。這個手法的本質是適當打破單件類的單件性約束,把單件類的構造函數改為protected,使我們能夠從單件類派生出一個Fake子類,然后為單件類引入一個靜態調包函數SupersedeInstance(),以允許單件類中的靜態成員變量被“調包”成Fake子類對象。下圖表明了這一手法。

      同樣必須強調的是,在C++中的使用這個手法的時候,必須保證沒有資源泄漏。

      2.5 如果CollaboratorClass本身就處于繼承體系中,怎么辦?

      先設想一下CollaboratorClass本身存在基類的情況,如下圖所示。


    如果使用“Virtual and Override”手法來偽造合作者類,那么不存在任何問題,我們可以用下面的圖來表示。

      另一方面,如果想使用“接口提取”手法的話,那么一種比較好的策略是使用“外覆類”(Wrapper Class),如下圖所示。

      2.6 當CollaboratorClass是標準類庫的一員時,怎么辦?

      有些時候,我們的被測類所依賴的是特定操作系統(Windows, Unix, 或Mac)、特定標準規范(.NET,或J2EE)、特定函數庫或類庫(如POSIX API和Win32 API)及特定用戶界面(CUI或者GUI)所提供的功能時,這實際上是引入了對特定平臺的依賴性,而這往往都是在提示我們:應該加入一個更高層次的抽象(也即一個間接層,indirection),從而將這種對特定平臺的依賴隱藏到這個抽象之后。換句話說,我們應該引入一個接口,來使我們的代碼具有平臺無關性,如下圖所示。

      2.7 怎樣測試抽象接口?

      假設我們的系統中定義了一個抽象接口ServiceInterface,系統中有兩個類(分別是ServiceImpl1和ServiceImpl2)實現了這個接口。現在,我們希望為ServiceInteface抽象接口編寫一個通用的測試類,這個測試類不僅能測試當前已經實現該接口的類,而且可以不加修改地應用于將來實現ServiceInteface接口的類。應該怎么辦呢?下圖展示了一種可能的方案。

      上圖中,ServiceInterfaceTestFixture測試類中的測試方法都是基于ServiceInterface來進行測試的,不依賴于其具體實現類。這樣就保證了僅測試抽象接口所定義的行為。當將來系統引入ServiceInteface的新的實現類時,只需要從ServiceInterfaceTestFixture類再派生出一個新子類,并實現createServiceInstance()方法來創建相應的對象即可。

    posted on 2011-10-09 22:08 順其自然EVO 閱讀(337) 評論(0)  編輯  收藏 所屬分類: 測試學習專欄

    <2011年10月>
    2526272829301
    2345678
    9101112131415
    16171819202122
    23242526272829
    303112345

    導航

    統計

    常用鏈接

    留言簿(55)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    搜索

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 亚洲色偷精品一区二区三区| 亚洲欧洲尹人香蕉综合| 亚洲爆乳少妇无码激情| 一二三四在线播放免费观看中文版视频| 亚洲a一级免费视频| 99精品视频在线免费观看| 无码专区—VA亚洲V天堂| 免费一级毛片无毒不卡| 亚洲午夜久久影院| 精品免费久久久久久久| 亚洲一区二区三区久久| 日韩在线视频免费看| 国产成人亚洲精品91专区高清 | 外国成人网在线观看免费视频| 亚洲成av人影院| 黄网站色在线视频免费观看| 亚洲一区动漫卡通在线播放| 日本免费高清一本视频| 国产精品免费αv视频| 亚洲精品无码久久久影院相关影片| 嫩草影院在线播放www免费观看| 亚洲国产精品不卡在线电影| 国产精品视频免费观看| 亚洲欧美日韩中文无线码 | 国产成人精品免费视频网页大全 | 免费在线观看污网站| CAOPORN国产精品免费视频| 亚洲国产精品SSS在线观看AV| 99在线在线视频免费视频观看| 亚洲天堂一区二区三区| 国产精品深夜福利免费观看 | 一级毛片不卡片免费观看| 最新国产成人亚洲精品影院| 免费一看一级毛片全播放| 免费在线观看一级片| 日本亚洲色大成网站www久久| 亚洲精品岛国片在线观看| 99精品视频在线观看免费专区| 亚洲av无码一区二区三区人妖| 国产亚洲人成无码网在线观看 | 亚洲视频在线视频|