May 11 14:10~16:00
不少鄉民都有這樣的經驗,當單元測試寫多了之後才驚覺幫測試案例取個好名字很重要。如果名字取得不好,開發人員必須藉由閱讀測試案例程式碼來推敲測試案例的目的。不但耗費時間,而且一不小心還會推敲錯誤,因而拉長除錯時間。
前陣子在調整【單元測試與持續整合】課程的教材,增加了如何幫單元測試取個好名字的內容。參考〈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()
- 範例:isAdult_False_IfAgeLessThan18()、
相較於第一組,第二組的單元測試名稱包含測試環境的狀態與預期行為。如果測試案例執行錯誤,開發人員比較容易從測試案例的名字去推敲這個測試案例的用途,有助於縮短除錯的時間。
***
第三組:待測功能(feature)作為測試案例名稱
前兩組命名方式都包含(待測)函數名稱(method name),這會引發一個小問題,萬一函數名稱在重構之後改了名字,開發人員需要手動將測試案例的名字也改掉。這是一件很麻煩的事情,而且通常無法靠自動化重構工具幫忙。因此,有人就想出測試案例名稱不需要包含待測函數名稱,而是應該以「想要測試什麼功能」(feature being tested)為命名方式。依據是否在測試案例前面加上test,又可以分為下列兩種格式:
- test[Feature being tested]
- 範例:testIsNotAnAdultIfAgeLessThan18()、testFailToWithdrawMoneyIfAccountIsInvalid()
- Feature being tested
- 範例:IsNotAnAdultIfAgeLessThan18()、
FailToWithdrawMoneyIfAccountIsInvalid()
- 範例:IsNotAnAdultIfAgeLessThan18()、
鄉民們可以嘗試比較一下用中文「念出」下列測試案例:
- 第二組:isAdult_AgeLessThan18_False()
- isAdult()函數,當年齡小於18,回傳值為false
- 第三組:testIsNotAnAdultIfAgeLessThan18()
- 如果年齡小於18,則不算成人
很明顯第二組的方式念起來有一點點偏向「實作」面的敘述,而第三組則是比較偏向從「需求」或是「行為」的角度來描述。
***
第四組:預期行為加上測試狀態
第四組命名方式和第二組很像,主要差別在於將(待測)函數名稱(method name)拿掉,並分別在預期行為(expected behavior)與執行測試案例的狀態(state under test)前面加上Should…When或When…Then (or Expect),讓測試案例念起來比較像口語化的英文。依據採用Should..When或是When…Then的不同,可分為以下兩種格式:
- Should_ExpectedBehavior_When_StateUnderTest
- 範例:Should_ThrowException_When_AgeLessThan18()、
Should_FailToWithdrawMoney_When_InvalidAccount()
- 範例:Should_ThrowException_When_AgeLessThan18()、
- When_StateUnderTest_Then_ExpectedBehavior
- 範例:When_AgeLessThan18_Then_isAdultAsFalse()、
When_InvalidAccount_Then_WithdrawMoneyToFail
- 範例:When_AgeLessThan18_Then_isAdultAsFalse()、
***
第五組:GWT格式
最後一組採用行為驅動開發(Behavior-Driven Development;BDD)普遍使用的Given-When-Then格式做為單元測試案例的名稱:
- Given_StateUnderTest_When_ActionUnderTest_Then_ExpectedOutcomes
- 範例:Given_UserIsAuthenticated_And_InvalidAccountNumberIsUsed_When_WithdrawMoney_Then_TransactionsWillFail()。
- 用中文可以念成:「假定使用者身分已驗證且提供錯誤帳號,當執行轉帳,則交易將會失敗。
相較於前面四組的寫法,GWT更接近口語但是寫起來卻是一長串,有些人覺得在單元測試的層級上使用GWT格式好像有點太囉嗦,因此喜好選擇第四或是第三組的寫法。
***
友藏內心獨白:第一組的寫法好像弱掉了。
沒有留言:
張貼留言