| 導航:起始頁 > Dive Into Python > 單元測試 > 完備性檢測 (Testing for sanity) | << >> | ||||
深入 Python :Dive Into Python 中文版Python 從新手到專家 [Dip_5.4b_CPyUG_Release] |
|||||
你經常會發現一組代碼中包含互逆的轉換函數,一個把 A 轉換為 B ,另一個把 B 轉換為 A。在這種情況下,創建“完備性檢測”可以使你在由 A 轉 B 再轉 A 的過程中不會出現丟失精度或取整等錯誤。
考慮這個要求:
class SanityCheck(unittest.TestCase): def testSanity(self): """fromRoman(toRoman(n))==n for all n""" for integer in range(1, 4000):![]()
numeral = roman.toRoman(integer) result = roman.fromRoman(numeral) self.assertEqual(integer, result)
| 你已經見到過 range 函數,但這里它以兩個參數被調用,返回了從第一個參數 (1) 開始到但不包括 第二個參數 (4000) 的整數列表。因此,1..3999 就是準備轉換為羅馬數字表示的有效值列表。 | |
| 我想提一下,這里的 integer 并不是一個 Python 關鍵字,而只是沒有什么特別的變量名。 | |
| 這里的測試邏輯顯而易見:把一個數 (integer) 轉換為羅馬數字表示的數 (numeral),然后再轉換回來 (result) 并確保最后的結果和最初的數是同一個數。如果不是,assertEqual 便會引發異常,測試也便立刻失敗。如果所有的結果都和初始數一致,assertEqual 將會保持沉默,整個 testSanity 方法將會最終也保持沉默,測試則將會被認定為通過。 |
最后兩個要求和其他的要求不同,似乎既武斷而又微不足道:
事實上,它們確實有點武斷,譬如你完全可以讓 fromRoman 接受小寫和大小寫混合的輸入;但他們也不是完全武斷;如果 toRoman 總是返回大寫的輸出,那么 fromRoman 至少應該接受大寫字母輸入,不然 “完備性檢測” (要求 #6) 就會失敗。不管怎么說,只 接受大寫輸入還是武斷的,但就像每個系統都會告訴你的那樣,大小寫總會出問題,因此事先規定這一點還是有必要的。既然有必要規定,那么也就有必要測試。
class CaseCheck(unittest.TestCase): def testToRomanCase(self): """toRoman should always return uppercase""" for integer in range(1, 4000): numeral = roman.toRoman(integer) self.assertEqual(numeral, numeral.upper())def testFromRomanCase(self): """fromRoman should only accept uppercase input""" for integer in range(1, 4000): numeral = roman.toRoman(integer) roman.fromRoman(numeral.upper())
![]()
self.assertRaises(roman.InvalidRomanNumeralError, roman.fromRoman, numeral.lower())
| 關于這個測試用例最有趣的一點不在于它測試了什么,而是它不測試什么。它不會測試 toRoman 的返回值是否正確或者一致;這些問題由其他測試用例來回答。整個測試用例僅僅測試大寫問題。你也許覺得應該將它并入到完備性測試,畢竟都要遍歷整個輸入值范圍并調用 toRoman。[11]但是這樣將會違背一條基本規則:每個測試用例只回答一個問題。試想一下,你將這個測試并入到完備性測試中,然后遇到了測試失敗。你還需要進一步分析以便判定測試用例的哪部分出了問題。如果你需要分析方能找出問題所在,無疑你的測試用例在設計上出了問題。 | |
| 這有一個和前面相似的情況:盡管 “你知道” toRoman 總是返回大寫字母,你還是需要把返回值顯式地轉換成大寫字母后再傳遞給只接受大寫的 fromRoman 進行測試。為什么?因為 toRoman 只返回大寫字母是一個獨立的需求。如果你改變了這個需求,例如改成總是返回小寫字母,那么 testToRomanCase 測試用例也應作出調整,但這個測試用例應該仍能通過。這是另外一個基本規則:每個測試用例必須可以與其他測試用例隔離工作,每個測試用例是一個“孤島”。 | |
| 注意你并沒有使用 fromRoman 的返回值。這是一個有效的 Python 語法:如果一個函數返回一個值,但沒有被使用,Python 會直接把這個返回值扔掉。這正是你所希望的,這個測試用例并不對返回值進行測試,只是測試 fromRoman 接受大寫字母而不引發異常。 | |
| 這行有點復雜,但是它與 ToRomanBadInput 和 FromRomanBadInput 測試很相似。 你在測試以特定值 (numeral.lower(),循環中目前羅馬數字的小寫版) 調用特定函數 (roman.fromRoman) 會確實引發特定的異常 (roman.InvalidRomanNumeralError)。如果 (在循環中的每一次) 確實如此,測試通過;如果有一次不是這樣 (比如引發另外的異常或者不引發異常),測試失敗。 |
在下一章中,你將看到如何編寫可以通過這些測試的代碼。
[11] “除了誘惑什么我都能抗拒。 (I can resist everything except temptation.)”――Oscar Wilde
<< 負面測試 (Testing for failure) |
| 1 | 2 | 3 | 4 | 5 | 6 | |
測試優先編程 >> |