單元測試設計模式

單元測試案例的基本流程有三:測試準備、執行標的程式、斷言驗證。 UDE風格建議在撰寫單元測試方法時,加上區塊注解如下,你可以選擇這些區塊是否存在,但它們應該依序出現。 在較大的測試案例函式中使用區塊註解,可以讓各類流程代碼的執行順序一目了然;也可以幫助開發人員思考是否有做到「一項概念、一個測試」。

@Test
public void test_case01(){
  // ! [TEST_INIT]  準備XXXX 測試資料
  this.mockQueryResult(……..);

  // ! [TEST_PRE-ASSERT] 預期執行應有Exception.
  this.expectRisBusinessException(“…….”);

  // ! [TEST_INVOKE] 
  this.target.verifyAndQuery();

  // ! [TEST_INVOKE & ASSERT]
  // ! [TEST_ASSERT] 驗證XXXX資料
  Assert.fail(“不應執行到此”);
}

以下幾個常見的「斷言驗證」、「測試準備」PATTERN 可以做為實作參考。

斷言驗證Pattern

斷言驗證即是以程式碼確認執行結果是否如預期執行。沒有撰寫適當驗證的測試,只能保證程式可編譯、可執行,但是對程式正確性沒有任何保證。這裡介紹的Pattern都是實作測試時常見的手法,可以依測試案例的性質組合應用。

結果狀態斷言

最基本的斷言方式,驗證執行標的程式後,其回傳值或內部狀態是否與期望一致。實作上,org.junit.Assert.* 已經提供大部分比對所需的函式。此類斷言會出現在 [TEST_ASSERT] 區塊中。

  // ! [TEST_INVOKE & ASSERT]
  assertEquals( ‘1’ , target.get(‘a’));
  assertEquals( ‘2’ , target.get(‘b’));}

防衛斷言

在執行標的之前所下的斷言驗證。此類斷言會出現在 [TEST_PRE-ASSERT] 區塊中。 若是黑箱型的單元測試,可能是驗證對象的初始狀態;若是白箱型,可能是對一些TEST-TRAP的定義、宣告,以確保執行標的有進入某個正常/異常流程。

  // ! [TEST_INIT]  
  List<String>   list   = new ArrayList<>();
  // ! [TEST_PRE-ASSERT] 預期原本為空
  assertTrue (list.isEmpty());
  // ! [TEST_INVOKE]
  list.add(“TEST”);
  // ! [TEST_ASSERT] 預期加入元素後,LIST不為空
  assertFalse(list.isEmpty());
  // ! [TEST_PRE-ASSERT] 預期執行應有Exception.
  this.expectRisBusinessException(“…….”);
  // ! [TEST_INVOKE] 
  this.target.verifyAndQuery();

差值斷言

有時候對象資料的初始內容,無法完全由測試程式控制,結果也不是明確的固定值。這時可以用比較的方式來確認結果是否正確。

  // ! [TEST_INIT]
  final  long  time1 = Now.now();
  // ! [TEST_INVOKE]
  sleep(100);
  // ! [TEST_ASSERT] 確認 sleep 了 100 ms, 但是無法精準判定
  final  long  time2 = Now.now();
  assertTrue( time2 – time1  >= 100 )

自定義斷言

其實就是驗證邏輯的封裝,當驗證邏輯比較複雜,又出現多次,可以自行包裝斷言行為。通常會在 [TEST_ASSERT] 區塊中被呼叫,有時也會以 [TEST_INVOKE & ASSERT] 型式使用。 常見用途像是:比較對象未實作Equals;只需要對目標做部分比對;比對對象的類別完全不同。自行封裝過的驗證邏輯,也更容易輸出有意義的驗證失敗訊息。

交互斷言

與其它斷言模式不同,交互斷言並不驗證代碼結果正確性,而是驗證與其它MOCK對象的交互行為正確性。搭配mockito使用,可以利用verify() 或自訂Answer類別以驗證元件呼叫順序。通常會用於業務邏輯驗證。

Fixture Pattern

關於Fixture的模式,比較像是概念上的整理,它告訴我們在整個測試週期上,有哪些部分應該試著做抽離與封裝。但是它們較沒有固定的實作方式,而各自著重的細節也需隨專案/組件/測試性質調整。

參數化建立函式(Parameterized Creation Method)

針對單一性質所做的測試中,Entity類型的物件,通常有許多屬性是無關緊要的。測試程式為了填充這些屬性,很容易有過多的程式碼。把這些程式封裝為參數化建立函式,即可大幅簡化測試原始碼。部分屬性的特殊性質,如相依性,唯一性,也可以在其中得到保證。 推而廣之,針對同樣的驗證邏輯,僅是輸入與預期結果不同時,我們也可以用「參數化測試」達成目的。Junit與TestNG都有相對的實作方式¸,像是Junit的Parameterized / @Parameters。UDE也有類似的輔助套件UdeTestFixture / @TestData,容後介紹。

Object Mother (OM)

Object Mother 進一步消除測試所需物件的重複建立,簡單來說,它可以視為一堆參數化建立函式的聚合體。 OM的實作,與團隊的測試規劃息息相關。最簡單的規劃模式是每一個業務邏輯類別有自己的OM,但程式的重複性也較高。

自動清理

在每一個測試結束後的tearDown方法中,應該試著把所有因測試而異動的共用資料(如資料庫、STATIC屬性)進行復原。但是一個測試類別中,每一個測試方法所需的還原項目可能多少有些出入。 自動清理模式是在創建測試資料物件時,即註冊相關資料,可使這些資料在測試結束後透過統一的單一命令執行清理/還原作業。所以此模式也通常與OM類別有高度相關性。

results matching ""

    No results matching ""