l

2016年5月12日 星期四

單元測試命名方法

May 11 14:10~16:00

螢幕截圖 2016-05-11 15.52.23

 

不少鄉民都有這樣的經驗,當單元測試寫多了之後才驚覺幫測試案例取個好名字很重要。如果名字取得不好,開發人員必須藉由閱讀測試案例程式碼來推敲測試案例的目的。不但耗費時間,而且一不小心還會推敲錯誤,因而拉長除錯時間。

前陣子在調整【單元測試與持續整合】課程的教材,增加了如何幫單元測試取個好名字的內容。參考〈7 Popular Unit Test Naming Conventions〉這篇文章所提的七種方法,一共整理出九種常見的命名方式,分成五組來介紹。

***

第一組:待測函數名稱

最常見的兩種命名方式就是用待測函數名稱當作測試案例名稱,例如想要測試withdraw這個函數:

  • testMethodName
    • 範例:testWithdraw()
  • MethodName
    • 範例:withdraw()

這兩種方式大致相同,是最簡單也是初學者最常使用的命名方式,唯一的差別只在於是否在待測函數名稱前面加上test這幾個字。

***

第二組:待測函數名稱加上測試狀態與預期行為

這一組命名方式的每個單元測試名稱包含三個部分,分別是函數名稱(method name)執行測試案例的狀態(state under test),以及預期行為(expected behavior)。依據後兩者出現的先後順序不同,可分為以下兩種格式:

  • MethodName_StateUnderTest_ExpectedBehavior
    • 範例:isAdult_AgeLessThan18_False()、withdraw_InvalidAccount_ExceptionThrown()
  • MethodName_ExpectedBehavior_StateUnderTest
    • 範例:isAdult_False_IfAgeLessThan18()、
      withdraw_ThrowsException_IfInvalidAccount()

相較於第一組,第二組的單元測試名稱包含測試環境的狀態與預期行為。如果測試案例執行錯誤,開發人員比較容易從測試案例的名字去推敲這個測試案例的用途,有助於縮短除錯的時間。

***

第三組:待測功能(feature)作為測試案例名稱

前兩組命名方式都包含(待測)函數名稱(method name),這會引發一個小問題,萬一函數名稱在重構之後改了名字,開發人員需要手動將測試案例的名字也改掉。這是一件很麻煩的事情,而且通常無法靠自動化重構工具幫忙。因此,有人就想出測試案例名稱不需要包含待測函數名稱,而是應該以「想要測試什麼功能」(feature being tested)為命名方式。依據是否在測試案例前面加上test,又可以分為下列兩種格式:

  • test[Feature being tested]
    • 範例:testIsNotAnAdultIfAgeLessThan18()、testFailToWithdrawMoneyIfAccountIsInvalid()
  • Feature being tested
    • 範例:IsNotAnAdultIfAgeLessThan18()、
      FailToWithdrawMoneyIfAccountIsInvalid()

鄉民們可以嘗試比較一下用中文「念出」下列測試案例:

  • 第二組:isAdult_AgeLessThan18_False()
    • isAdult()函數,當年齡小於18,回傳值為false
  • 第三組:testIsNotAnAdultIfAgeLessThan18()
    • 如果年齡小於18,則不算成人

很明顯第二組的方式念起來有一點點偏向「實作」面的敘述,而第三組則是比較偏向從「需求」或是「行為」的角度來描述。

***

第四組:預期行為加上測試狀態

第四組命名方式和第二組很像,主要差別在於將(待測)函數名稱(method name)拿掉,並分別在預期行為(expected behavior)執行測試案例的狀態(state under test)前面加上Should…WhenWhen…Then (or Expect),讓測試案例念起來比較像口語化的英文。依據採用Should..When或是When…Then的不同,可分為以下兩種格式:

  • Should_ExpectedBehavior_When_StateUnderTest
    • 範例:Should_ThrowException_When_AgeLessThan18()、
      Should_FailToWithdrawMoney_When_InvalidAccount()
  • When_StateUnderTest_Then_ExpectedBehavior
    • 範例:When_AgeLessThan18_Then_isAdultAsFalse()、
      When_InvalidAccount_Then_WithdrawMoneyToFail

***

第五組:GWT格式

最後一組採用行為驅動開發(Behavior-Driven Development;BDD)普遍使用的Given-When-Then格式做為單元測試案例的名稱:

  • Given_StateUnderTest_When_ActionUnderTest_Then_ExpectedOutcomes
    • 範例:Given_UserIsAuthenticated_And_InvalidAccountNumberIsUsed_When_WithdrawMoney_Then_TransactionsWillFail()。
    • 用中文可以念成:「假定使用者身分已驗證且提供錯誤帳號,當執行轉帳,則交易將會失敗。

相較於前面四組的寫法,GWT更接近口語但是寫起來卻是一長串,有些人覺得在單元測試的層級上使用GWT格式好像有點太囉嗦,因此喜好選擇第四或是第三組的寫法。

***

友藏內心獨白:第一組的寫法好像弱掉了。

沒有留言:

張貼留言