Python之自动单元测试之一(unittest使用实例)
?
?
1 測試用例TestCase
軟件測試中最基本的組成單元是測試用例(test case),PyUnit使用TestCase類來表示測試用例,并要求所有用于執行測試的類都必須從該類繼承。TestCase子類實現的測試代碼應該是自包含(self contained)的,也就是說測試用例既可以單獨運行,也可以和其它測試用例構成集合共同運行。
TestCase在PyUnit測試框架中被視為測試單元的運行實體,Python程序員可以通過它派生自定義的測試過程與方法(測試單元),利用Command和Composite設計模式,多個TestCase還可以組合成測試用例集合。PyUnit測試框架在運行一個測試用例時,TestCase子類定義的setUp()、runTest()和tearDown()方法被依次執行,最簡單的測試用例只需覆蓋runTest()方法來執行特定的測試代碼就可以了,如例4所示:
static_single.pyimport unittest# 執行測試的類class WidgetTestCase(unittest.TestCase): def runTest(self): widget = Widget() self.assertEqual(widget.getSize(), (40, 40))而要在PyUnit測試框架中構造上述WidgetTestCase類的一個實例,應該不帶任何參數調用其構造函數:
testCase = WidgetTestCase()一個測試用例通常只對軟件模塊中的一個方法進行測試,采用覆蓋runTest()方法來構造測試用例在PyUnit中稱為靜態方法,如果要對同一個軟件模塊中的多個方法進行測試,通常需要構造多個執行測試的類,如例5所示:
static_multi.pyimport unittest# 測試getSize()方法的測試用例class WidgetSizeTestCase(unittest.TestCase): def runTest(self): widget = Widget() self.assertEqual(widget.getSize(), (40, 40))# 測試resize()方法的測試用例class WidgetResizeTestCase(unittest.TestCase): def runTest(self): widget = Widget() widget.resize(100, 100) self.assertEqual(widget.getSize(), (100, 100))采用靜態方法,Python程序員不得不為每個要測試的方法編寫一個測試類(該類通過覆蓋runTest()方法來執行測試),并在每一個測試類中生成一個待測試的對象。在為同一個軟件模塊編寫測試用例時,很多時候待測對象有著相同的初始狀態,因此采用上述方法的Python程序員不得不在每個測試類中為待測對象進行同樣的初始化工作,而這往往是一項費時且枯燥的工作。
一種更好的解決辦法是采用PyUnit提供的動態方法,只編寫一個測試類來完成對整個軟件模塊的測試,這樣對象的初始化工作可以在setUp()方法中完成,而資源的釋放則可以在tearDown()方法中完成,如例6所示:
dynamic.pyimport unittest# 執行測試的類class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget() def tearDown(self): self.widget.dispose() self.widget = None def testSize(self): self.assertEqual(self.widget.getSize(), (40, 40)) def testResize(self): self.widget.resize(100, 100) self.assertEqual(self.widget.getSize(), (100, 100))采用動態方法最大的好處是測試類的結構非常好,用于測試一個軟件模塊的所有代碼都可以在同一個類中實現。動態方法不再覆蓋runTest()方法,而是為測試類編寫多個測試方法(按習慣這些方法通常以test開頭),在創建TestCase子類的實例時必須給出測試方法的名稱,來為PyUnit測試框架指明運行該測試用例時究竟應該調用測試類中的哪個方法:
sizeTestCase = WidgetTestCase("testSize")resizeTestCase = WidgetTestCase("testResize")2 測試用例集TestSuite
完整的單元測試很少只執行一個測試用例,開發人員通常都需要編寫多個測試用例才能對某一軟件功能進行比較完整的測試,這些相關的測試用例稱為一個測試用例集,在PyUnit中是用TestSuite類來表示的。
在創建了一些TestCase子類的實例作為測試用例之后,下一步要做的工作就是用TestSuit類來組織它們。PyUnit測試框架允許Python程序員在單元測試代碼中定義一個名為suite()的全局函數,并將其作為整個單元測試的入口,PyUnit通過調用它來完成整個測試過程。
def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase("testSize")) suite.addTest(WidgetTestCase("testResize")) return suite也可以直接定義一個TestSuite的子類,并在其初始化方法(__init__)中完成所有測試用例的添加:
class WidgetTestSuite(unittest.TestSuite): def __init__(self): unittest.TestSuite.__init__(self, map(WidgetTestCase, ("testSize", "testResize")))這樣只需要在suite()方法中返回該類的一個實例就可以了:
def suite(): return WidgetTestSuite()如果用于測試的類中所有的測試方法都以test開,Python程序員甚至可以用PyUnit模塊提供的makeSuite()方法來構造一個TestSuite:
def suite(): return unittest.makeSuite(WidgetTestCase, "test")在PyUnit測試框架中,TestSuite類可以看成是TestCase類的一個容器,用來對多個測試用例進行組織,這樣多個測試用例可以自動在一次測試中全部完成。事實上,TestSuite除了可以包含TestCase外,也可以包含TestSuite,從而可以構成一個更加龐大的測試用例集:
suite1 = mysuite1.TheTestSuite()suite2 = mysuite2.TheTestSuite()alltests = unittest.TestSuite((suite1, suite2))3 實施測試
編寫測試用例(TestCase)并將它們組織成測試用例集(TestSuite)的最終目的只有一個:實施測試并獲得最終結果。PyUnit使用TestRunner類作為測試用例的基本執行環境,來驅動整個單元測試過程。Python開發人員在進行單元測試時一般不直接使用TestRunner類,而是使用其子類TextTestRunner來完成測試,并將測試結果以文本方式顯示出來:
runner = unittest.TextTestRunner()runner.run(suite)使用TestRunner來實施測試的例子如例7所示,
text_runner.pyfrom widget import Widgetimport unittest# 執行測試的類class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget() def tearDown(self): self.widget.dispose() self.widget = None def testSize(self): self.assertEqual(self.widget.getSize(), (40, 40)) def testResize(self): self.widget.resize(100, 100) self.assertEqual(self.widget.getSize(), (100, 100)) # 測試if __name__ == "__main__": # 構造測試集 suite = unittest.TestSuite() suite.addTest(WidgetTestCase("testSize")) suite.addTest(WidgetTestCase("testResize")) # 執行測試 runner = unittest.TextTestRunner() runner.run(suite)運行結果應該如下所示,表明執行了2個測試用例,并且兩者都通過了測試:
..----------------------------------------------------------------------Ran 2 tests in 0.000sOK如果對數據進行修改,模擬出錯的情形,將會得到如下結果:
.F==========================================FAIL: testResize (__main__.WidgetTestCase)----------------------------------------------------------------------Traceback (most recent call last): File "text_runner.py", line 15, in testResize self.assertEqual(self.widget.getSize(), (200, 100)) File "/usr/lib/python2.2/unittest.py", line 286, in failUnlessEqual raise self.failureException, /AssertionError: (100, 100) != (200, 100)----------------------------------------------------------------------Ran 2 tests in 0.001sFAILED (failures=1)默認情況下,TextTestRunner將結果輸出到sys.stderr上,但如果在創建TextTestRunner類實例時將一個文件對象傳遞給了構造函數,則輸出結果將被重定向到該文件中。在Python的交互環境中驅動單元測試時,使用TextTestRunner類是一個不錯的選擇。
PyUnit模塊中定義了一個名為main的全局方法,使用它可以很方便地將一個單元測試模塊變成可以直接運行的測試腳本,main()方法使用TestLoader類來搜索所有包含在該模塊中的測試方法,并自動執行它們。如果Python程序員能夠按照約定(以test開頭)來命名所有的測試方法,那就只需要在測試模塊的最后加入如下幾行代碼即可:
if __name__ == "__main__": unittest.main()使用main()方法來實施測試的例子如例8所示:
main_runner.pyfrom widget import Widgetimport unittest# 執行測試的類class WidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget() def tearDown(self): self.widget.dispose() self.widget = None def testSize(self): self.assertEqual(self.widget.getSize(), (40, 40)) def testResize(self): self.widget.resize(100, 100) self.assertEqual(self.widget.getSize(), (100, 100)) # 測試if __name__ == "__main__": unittest.main()為了使單元測試更具親合力,PyUnit軟件包中還提供了一個圖形界面測試腳本unittestgui.py,將其復制到當前目錄后,可以執行下面的命令來啟動該測試工具,對main_runner.py腳本中的所有測試用例進行測試:
python unittestgui.py main_runner四、小結
測試是保證軟件質量的關鍵,新的軟件開發方法要求程序員在編寫代碼前先編寫測試用例,并在軟件開發過程中不斷地進行單元測試,從而最大限度地減少缺陷(Bug)的產生。軟件單元測試是XP方法的基石,測試框架為程序員進行單元測試提供了統一的規范,Python程序員可以使用PyUnit作為軟件開發過程中的自動單元測試框架。
轉載于:https://www.cnblogs.com/star12111/p/7405967.html
總結
以上是生活随笔為你收集整理的Python之自动单元测试之一(unittest使用实例)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring源码-AOP(六)-自动代理
- 下一篇: iOS中使用OpenGL 实现增高功能