**於《有效的單元測試》系列文章3.2 測試替身的型別:
你見過了使用測試替身的各種原因,我們也暗示了有多種測試替身可供選擇。我們來仔細看看那些型別吧。圖3.3展示了這把大傘下的四種物件。
既然我們已經制定了測試替身的分類,現在就來認識一下它們,並了解相互的區別,以及運用它們的典型目的。我們先從最簡單的開始。
我這樣來定義它:樁(名詞),截斷的或非常短的物體。
這衍生出測試樁的精確定義。測試樁(簡稱樁或stub)的目的是用最簡單的可能實現來代替真實實現。最基本的實現例子就是乙個物件的所有方法都只有一行,且它們各自返回乙個適當的預設值。
假如你負責的**應當對自己的操作生成一段審計日誌,並通過叫做logger的介面寫入遠端日誌伺服器。假如logger介面僅僅定義了乙個方法來產生此類日誌,那麼logger介面的樁看起來是這樣:
有沒有注意到log()方法其實什麼都沒做?這是樁的典型例子——什麼都不做。畢竟,你正是對真實logger實現打樁,因為你在測試時完全不在乎日誌,那麼又何必真寫日誌呢?但是有時候什麼都不做也不行。例如,如果logger介面還定義了乙個方法來確定當前設定的日誌級別(log level),那麼樁實現看起來可能是這樣:
我們在這個類中硬編碼了getloglevel()方法,它總是返回loglevel.warn。有沒有搞錯?大部分情況下這絕對沒問題。畢竟,我們有三個充分的理由來使用測試樁代替真實logger實現:
1.?我們的測試不關心被測**所寫的日誌。
2.?我們沒有執行日誌伺服器,所以測試會悲劇地失敗。
3.?我們也不希望測試套件在控制台中輸出大量位元組(更別提將所有資料寫入檔案了)。
簡而言之,logger樁實現完美地滿足了我們的需要。
有時候,簡單的硬編碼返回語句和一堆空的void方法還不夠。有時候你至少需要填充一些行為,而有時候你需要測試替身根據收到的訊息種類來表現出不同的行為。這些情況下,你會借助偽造物件。
比起stub,偽造物件(簡稱fake)是一種更加複雜的測試替身。stub可以返回硬編碼值,而每個測試可能需要有差異地例項化來返回不同值,以模擬不同的場景。fake更像是真實事物的簡單版本,優化地偽造真實事物的行為,但是沒有***或使用真實事物的其他後果。
持久化物件是採用fake的典型例子。假設應用程式架構是這樣的:一些儲存物件提供持久化服務,它們知道如何儲存和查詢指定的物件型別。這種儲存物件可能提供的api如下:
對於使用儲存物件的應用程式,如果沒有這種測試替身,測試全都將試圖訪問真實的資料庫。要是對userrepository介面打樁,令其精確地返回測試所需,你就會感覺好一些。但是模擬更複雜的場景肯定會越發複雜。另一方面,由於userrepository介面足夠簡單,以至於你可以實現乙個愚蠢而簡單的記憶體資料庫,它只提供基本的資料型別。**清單3.4提供了乙個例子。
用這種另類實現來替換真實事物的優點在於,它像只鴨子那樣嘎嘎叫,還能搖擺,但它搖擺得比真鴨子要快——即使每次查詢乙個user時都迴圈乙個包含50個條目的列表。
測試樁和偽造物件往往是救命稻草,你可以在測試時用它們替換掉緩慢的真實事物,以及鞭長莫及的依賴。然而,這兩種基本的測試替身不總是夠用。有時你發現自己面對一堵牆,希望自己能像千里眼一樣看透它——為了驗證**行為符合預期。那些情況下,你可能會求助於測試間諜。
你如何測試下列方法?
大多數人會說,把這個那個傳進去,然後檢查返回值是什麼的。那可能沒問題。畢竟正確的返回值是你最關心的。那麼,下列方法又如何測試?
這裡並沒有返回值可以用來斷言。這個方法所做的事情是接收乙個列表和乙個謂詞(predicate),過濾列表中不滿足謂詞的條目。換句話說,驗證這個方法正常工作的唯一方式就是事後檢查列表。這就像警察臥底,然後匯報她看到的一切。通常你不用測試替身也能做到這一點。這個例子中你可以詢問list物件,看它是否包含你所期望的條目。
至於測試替身——我們正在討論的測試間諜(簡稱spy)——的方便之處在於,當沒有物件作為引數傳入時,通過它們的api也能揭示你想要了解的知識。**清單3.5顯示了這樣乙個例子。
我們先來看看上述**清單中的場景。被測物件是乙個分布式的日誌物件dlog,代表了一組dlogtarget。當向dlog寫入時,你應該向所有dlogtarget寫入相同的訊息。從測試的角度來看,事情有點尷尬,你無法知道指定的訊息是否被寫入,因為dlogtarget介面只定義了乙個方法write(),而且dlogtarget、consoletarget和remotetarget的真實實現也都沒有提供任何方法。
測試間諜登場了。**清單3.6展示了乙個精明的程式設計師如何鞭打他的女**去幹活。
這就是測試間諜的一切。像其他測試替身一樣,你將它們傳入。然後你令測試間諜記錄已傳送的訊息,並讓測試詢問測試間諜是否收到指定訊息。幹得漂亮!
簡而言之,測試間諜是一種測試替身,它用於記錄過去發生的情況,這樣測試在事後就知道所發生的一切。有時我們進一步利用這個概念,於是測試間諜就變成了全能的模擬物件。如果測試間諜像個臥底警察,那麼模擬物件就像滲入暴民的遠端控制機械人。這可能需要一些解釋……
模擬物件(簡稱mock)是特殊的spy。它是乙個在特定情景下可配置行為的物件。例如,userrepository介面的模擬物件可能被告之:當帶著引數123呼叫findbyid()時要返回null,而當帶著引數124呼叫findbyid()時要返回user的乙個例項。在這一點上,我們主要討論的是根據引數來對特定的方法呼叫打樁。
如果一旦任何意外發生時mock就立即使測試失敗,mock就能夠變得更加精確。例如,假設我們告訴了模擬物件如何應對帶著123或124的findbyid()呼叫,它就會嚴格按照指令工作。對於任何其他的呼叫——不論是呼叫不同的方法或者帶著另外的引數呼叫findbyid()——mock就會丟擲異常,直接使測試失敗。同樣,如果findbyid()被呼叫太多次,mock就會抱怨——除非我們告訴它允許呼叫任意次數——如果預期的呼叫沒發生,mock也會抱怨。
包括jmock、mockito和easymock在內的模擬物件庫已經是成熟的工具了,崇尚測試的程式設計師可以借助它們獲得力量。每個庫都有自己的行事風格,但基本上你可以用它們中任何乙個來完成所有的工作。
這並非模擬物件庫的全面教程,但是我們迅速看看**清單3.7中的例子,它展示了這種庫的具體用法。這裡我們使用jmock,因為我碰巧有個專案正在使用jmock。
在這樣一小段測試**中,這個例子展示了許多模擬物件庫用法的典型構造。首先,我們告訴庫要為指定介面建立乙個模擬物件。
在context.checking()中看似笨拙的**塊其實是測試在指導模擬的internet,告訴它應該期待哪些互動,以及如何應對這些互動。這種情況下,我們預期測試會帶著包含"langpair=en%7cfi"字串的引數呼叫get()方法一次,對此,mock應當返回指定字串。
最終,我們將mock傳給被測的translator物件,執行translator,然後斷言translator為我們的場景提供了正確的翻譯。
然而,這並非我們的全部斷言。如前所述,mock可以嚴格地判斷已經發生的預期互動。在模擬internet的例子中,mock嚴格地斷言它確實收到了一次帶有指定子字串引數的get()方法呼叫。
軟體測試的型別
1 正常測試 測試某個功能是否滿足需求的定義,功能是否正確,完備。2 邊界測試 對某個功能的邊界情況進行測試。3 異常測試 對某些功能來說,其邊界情況無法簡單的了解或某些操作不完全是正確的但又是可能發生的,類似這樣的情況需要書寫相關的異常測試 4 效能測試 檢查系統是否滿足在需求中所規定達到的效能,...
軟體測試的型別與測試方法
1.黑盒測試 黑盒測試也稱功能測試,不考慮程式內部結構和內部特性的情況下,通過測試來檢測每個功能是否都能正常使用。其中測試方法包括 功能測試 自動化測試 效能測試 安全測試。功能測試 冒煙測試 ui介面測試 回歸測試 相容性測試 效能測試 負載測試 壓力測試。它主要包括 併發效能測試 疲勞強度測試 ...
關於效能測試的測試型別
模擬系統在不同負載條件,系統的各項效能指標是否良好 關注點 首要是最佳使用者數量和最大使用者數量,然後還要關注各項效能指標的值 模擬負載超出了最大值之外的情況,看系統如何崩潰,目的是據此尋找改善使用者體驗的方案 關注點 系統在極限壓力時崩潰的原因 關注點 系統的最大使用者數,資料庫的儲存條目數量,表...