單元測試 – 使用Fake Object

抽取相依的物件並且覆寫

下面是一個範例,這個範例的SUT是Holiday.cs,如果今天是9/1就傳HappyBirthday,否則就傳No
因為單元測試應該要能夠具有隔離的特性,不可因為今天的日期不一樣而有不同的結果
所以”取得今天日期”,就會讓程式碼變得不可測試。

在下面的範例裡,我們可以看見如何使用假物件去測試不可測試的程式碼:
Holiday.cs

Test2Cs.cs

如何對現行沒有測試的PROD CODE加上unit test


這一個測試的重點在於:針對依賴的code,找出不可控制的地方並抽出來

  • 找到不可控制的依賴
  • 抽出方法
  • 把private改成protect virtual
  • 在測試專案新增子類繼承SUT
  • override protected方法並加開set方法
  • 測試SUT改測子類
  • set依賴的值

使用依賴注入

使用依賴注入來達成低藕合高內聚的程式碼
1. 針對相依的物件抽出interface,針對相依的值抽出field
2. 產生contructor,選要注入的field去依賴注入
3. 產生無參數的constructor,確保本來的程式無誤
4. 測試程式新增fake物件做interface,決定行為並注入SUT

所謂低藕合高內聚的程式碼也可以讓程式更容易被測試

用Fake Object來取代相依物件的方法

如果測試的資料都是固定的,可以在測試裡面創建假物件來注入SUT(測試目標)

以下為範例程式
AuthenticationServiceTests.cs

AuthenticationService.cs

單元測試 – 寫測試的基本原則

熱鍵

  • Alt+Enter: Quick+Action
  • Ctrl+R,M 抽方法出來
  • code snippets / live template
  • constructor: ctor
  • property:prop
  • console.writeLine)():cw
  • ctrl+R,F: Extract Field
  • Alt+Insert
  • 循環剪貼簿:ctrl+shift+V

測試替身有下面三種

  • stub:不做驗證,單純只做模擬相依物件的行為
  • mock:一開始就要把所有的都定義清楚,應該要呼叫那個方法,所有的值一定要一模一樣,否則就會報錯(嚴格,敏感,不穩定)
  • spy: 則是把所有的互動先做完,只驗要驗的,剩的沒有測就是都算過,差異是一個從嚴一個從寬。(寬鬆)

因此mock和spy本身含有驗證(Assertion),而stub本身只有在模擬相依的物件而已。

比較物件屬性的方式

比較兩邊的物件包含子物件完全相同

比較以expected為主的屬性去比較相對應的actual是否相同

測試範例如下

寫測試的規則



測試程式不含商業邏輯,所有的測試都應該是直述句
不應包含以下的元素:

  • prod business logic
  • 不含if, else, switch case等邏輯程式碼
  • 更不含try..catch
  • 不含for, while, foreach, do..while

善用assertion package

  • C#: expectedObjects,FluentAssertions
  • Java: Assert J

不要攤開properity做比較

寫測試一定要重構,不然在測試需求異動時和寫測試時會太花時間
因此不應該要有太多不會用到的資訊
讓測試的目標的意圖可以很明顯
要如何加快單元測試撰寫的速度很重要,這樣才有可能可以實踐單元測試
沒有時間是個問題,但是我們要去面對如何解決這個問題
要知道怎麼用工具怎麼寫比較快

單元測試的藝術-單元測試基礎

單元測試

一個單元代表的是系統中的工作單元或是一個使用案例
被測試的系統(System Under Test)我們稱做SUT或者Class Unit Test(CUT)
一個單元測試是一段程式呼叫一個工作單元,並驗證工作單元的一個具體最終結果。如果對這個最終結果的假設是錯誤的,那單元測試就失敗了。一個單元測試的範圍,可以小到一個方法,大到多個類別。

優秀單元測試的特質

  • 自動化,可被重覆執行的
  • 很容易被實現
  • 非臨時性的
  • 任何人都可以按鈕執行他
  • 執行速度快
  • 執行結果每次都是一致的
  • 能完全掌控被測試的單元
  • 完全被隔離的
  • 若執行失敗會有清楚的原因

整合測試

整合測試是一個有順序的測試過程,將軟硬體相結合並進行測試直到整個系統被整合在一起。也就是這個測試對被測試的單元並沒有完全的控制,而是使用該單元一個或多個真實依賴的相依物件,例如:時間,網路,資料庫,執行緒,亂數產生器等等。

一個單元測試通常包含了三個行為

  • 準備(Arrange):物件,建立物件,進行必要的設定
  • 操作(Act): 物件
  • 驗證(Asset): 某件事符合預期

Assert類別

  • Assert.True: 驗證一個布林條件,見Assert.False
  • Assert.AreEqual: 驗證傳回的值應相同
  • Assert.AreSame: 驗證兩個參數應指向同一個物件

使用參數來測試


使用TestCase標籤

Setup和tesrdown