目錄[-]
unittest 概覽舉個例子啟動測試Test Discovery測試環境unittest 與 doctest 一樣也是 Python 發行版自帶的包。如果你聽說過 PyUnit(OSC 開源項目頁面中就有 PyUnit 的頁面),那么這倆其實是同一個東西——PyUnit 是 unittest 的曾用名,因為 PyUnit 最早也是來源于 Kent 和 Erich 的 JUnit(xUnit 測試框架系列的 java 版本)
上一篇介紹的 doctest 不管是看起來還是用起來都顯得十分簡單,可以與源碼寫在一起,比較適合用作驗證性的功能測試。而本篇的 unittest 從名字上看,它是一個單元測試框架;從官方文檔的字數上看,它的能力應該比 doctest 強一些。
使用 unittest 的標準流程為:
從 unittest.TestCase 派生一個子類在類中定義各種以 “test_” 打頭的方法通過 unittest.main() 函數來啟動測試unittest 的一個很有用的特性是 TestCase 的 setUp() 和 tearDown() 方法,它們提供了為測試進行準備和掃尾工作的功能,聽起來就像上下文管理器一樣。這種功能很適合用在測試對象需要復雜執行環境的情況下。
這里依舊使用上篇中那個極簡的例子:unnecessary_math.py 文件中有一個multiply() 函數,功能與 * 操作符完全一樣。
test_um_test.py:
import unittestfrom unnecessary_math import multiplyclass TestUM(unittest.TestCase): def setUp(self): pass def test_number_3_4(self): self.assertEqual(multiply(3,4),12) def test_string_a_3(self): self.assertEqual(multiply('a',3),'aaa')if __name__ == '__main__': unittest.main()這個例子里,我們使用了 assertEqual() 方法。unittest 中還有很多類似的 assert 方法,比如 NotEqual、Is(Not)None、True(False)、Is(Not)Instance 等針對變量值的校驗方法;另外還有一些如 assertRaises()、assertRaisesRegex() 等針對異常、警告和 log 的檢查方法;以及如assertAlmostEqual() 等一些奇怪的方法。
較詳細的 assert 方法可以參考 unittest 的文檔頁面。
啟動測試
上例中的結尾處,我們定義了一個對 unittest.main() 的調用,因此這個腳本是可以直接運行的:
$ python test_um_test.py..--------------------------------------Ran 2 tests in 0.01sOK同樣 -v 參數是可選的,也可以在 unittest.main() 函數里直接指定:verbosity=1。
Test Discovery
這個分段標題我暫時沒想到好的翻譯方法,就先不翻了。
Test Discovery 的作用是:假設你的項目文件夾里面四散分布著很多個測試文件。當你做回歸測試的時候,一個一個地執行這些測試文件就太麻煩了。TestLoader.discover() 提供了一個可以在項目目錄下自動搜索并運行測試文件的功能,并可以直接從命令行調用:
$ cd PRoject_directory$ python -m unittest discoverdiscover 可用的參數有 4 個(-v -s -p -t),其中 -s 和 -t 都與路徑有關,如上例中提前 cd 到項目路徑的話這倆參數都可以無視;-v 喜聞樂見;-p 是 --pattern 的縮寫,可用于匹配某一類文件名。
測試環境
當類里面定義了 setUp() 方法的時候,測試程序會在執行每條測試項前先調用此方法;同樣地,在全部測試項執行完畢后,tearDown() 方法也會被調用。驗證如下:
import unittestclass simple_test(unittest.TestCase): def setUp(self): self.foo = list(range(10)) def test_1st(self): self.assertEqual(self.foo.pop(),9) def test_2nd(self): self.assertEqual(self.foo.pop(),9)if __name__ == '__main__': unittest.main()注意這里兩次測試均對同一個實例屬性 self.foo 進行了 pop() 調用,但測試結果均為 pass,即說明,test_1st 和test_2nd 在調用前都分別調用了一次 setUp()。
那如果我們想全程只調用一次 setUp/tearDown 該怎么辦呢?就是用 setUpClass() 和 tearDownClass() 類方法啦。注意使用這兩個方法的時候一定要用 @classmethod 裝飾器裝飾起來:
import unittestclass simple_test(unittest.TestCase): @classmethod def setUpClass(self): self.foo = list(range(10)) def test_1st(self): self.assertEqual(self.foo.pop(),9) def test_2nd(self): self.assertEqual(self.foo.pop(),8)if __name__ == '__main__': unittest.main()這個例子里我們使用了一個類級別的 setUpClass() 類方法,并修改了第二次 pop() 調用的預期返回值。運行結果顯示依然是全部通過,即說明這次在全部測試項被調用前只調用了一次 setUpClass()。
再往上一級,我們希望在整個文件級別上只調用一次 setUp/tearDown,這時候就要用 setUpModule() 和 tearDownModule() 這兩個函數了,注意是函數,與 TestCase 類同級:
import unittestdef setUpModule(): passclass simple_test(inittest.TestCase): ...一般 assert*() 方法如果拋出了未被捕獲的異常,那么這條測試用例會被記為 fail,測試繼續進行。但如果異常發生在 setUp() 里,就會認為測試程序自身存在錯誤,后面的測試用例和tearDown() 都不會再執行。即,tearDown() 僅在setUp() 成功執行的情況下才會執行,并一定會被執行。
最后,這兩個方法的默認實現都是什么都不做(只有一句 pass),所以覆蓋的時候直接寫新內容就可以了,不必再調用父類的此方法。
新聞熱點
疑難解答