1. 程式人生 > >Python自動化測試框架PyUnit==Unittest

Python自動化測試框架PyUnit==Unittest

Python中進行單元測試需要用到自動單元測試框架PyUnit,Python2.1及其以後的版本都將PyUnit作為一個標準模組(即python的unittest模組),如果你很out,那麼你需要從PyUnit網站下載原始碼安裝後才能使用。

一、Python單元測試範例

        測試最基本的原理是比較預期結果是否與實際執行結果相同,如果相同則測試成功,否則測試失敗。為了更好地理解自動測試框架PyUnit,下面會以對Widget類進行測試為例說明之:
  1. #widget.py
  2. #將要被測試的類Widget
  3. class Widget:  
  4.     def __init__(self, size = (
    4040)):  
  5.         self._size = size  
  6.     def getSize(self):  
  7.         returnself._size  
  8.     def resize(self, width, height):  
  9.         if width < 0or height < 0:  
  10.             raise ValueError, "illegal size"
  11.         self._size = (width, height)  
  12.     def dispose(self):  
  13.         pass


二、測試用例TestCase

        軟體測試中最基本的組成單元式測試用例(test case),PyUnit使用TestCase類來表示測試用例,並要求所有用於執行測試的類都必須從該類繼承。TestCase子類實現的測試程式碼應該是自包含的(self contained),即測試用例既可以單獨執行,也可以和其它測試用例構成集合共同執行。TestCase類中常用的函式或方法有:

       setUp:進行測試前的初始化工作。
       tearDown:執行測試後的清除工作。
       failedinfo:表示不成立列印資訊faliedinfo,為可選引數。
       self.assertEqual(value1, value2, failedinfo):會無條件的導致測試失敗,不推薦使用。
       self.assertTrue(, failedinfo):斷言value1 == value2。
       self.assertFalse(, failedinfo):斷言value為真。
       self.assertRaises(ValueError, self.widget.resize, -1, -1):
斷言肯定發生異常,如果沒發生異常,則為測試失敗。引數1為異常,引數2為丟擲異常的呼叫物件,其餘引數為傳遞給可呼叫物件的引數。

        TestCase在PyUnit測試框架中被視為測試單元的執行實體,Python程式設計師可以通過它派生自定義的測試過程與方法(測試單元),利用Command和Composite設計模式,多個TestCase還可以組合成測試用例集合。PyUnit測試框架在執行一個測試用例時,TestCase子類定義的setUp()、runTest()和tearDown()方法被依次執行,最簡單的測試用例只需要覆蓋runTest()方法來執行特定的測試程式碼就可以了。

1、靜態方法

        一個測試用例只對軟體模組中一個方法進行測試,採用覆蓋runTest()方法來構造測試用例,這在PyUnit中稱之為靜態方法,舉例說明如下:
  1. #static.py
  2. from widget import Widget  
  3. import unittest  
  4. #執行測試的類
  5. class WidgetTestCase(unittest.TestCase):  
  6.     def runTest(self):  
  7.         widget = Widget()  
  8.         self.assertEqual(widget.getSize(), (4040))  
  9. #測試
  10. if __name__ == "__main__":  
  11.     testCase = WidgetTestCase()  
  12.     testCase.runTest()  

        如果採用靜態方法,Python程式設計師就不得不為每個要測試的方法編寫一個測試類,該類通過覆蓋runTest()方法來執行測試,並在每個測試類中生成一個待測試的物件,這樣會非常繁瑣與笨拙。

2、動態方法

        鑑於靜態方法的缺陷,PyUnit提供了另一種高帥富的解決方法,即動態方法,只編寫一個測試類來完成對整個軟體模組的測試,這樣物件的初始化工作可以在setUp()方法中完成,而資源的釋放則可以在tearDown()方法中完成,舉例說明如下:
  1. #dynamic.py     
  2. from widget import Widget  
  3. import unittest  
  4. class WidgetTestCase(unittest.TestCase):  
  5.     def setUp(self):  
  6.         self.widget = Widget()  
  7.     def tearDown(self):  
  8.         self.widget.dispose()  
  9.         self.widget = None
  10.     def testSize(self):  
  11.         self.assertEqual(self.widget.getSize(), (4040))  
  12.     def testResize(self):  
  13.         self.widget.resize(100100)  
  14.         self.assertEqual(self.widget.getSize(), (100100))  

        動態方法不再覆蓋runTest()方法,而是為測試類編寫多個測試方法,按照慣例這些方法通常以test開頭但這不是必須的,在建立TestCase子類的例項時必須給出測試方法的名稱來為PyUnit測試框架指明執行該測試用例時應該呼叫測試類中的哪些方法,這通常會結合測試用例集TestSuite一起使用。

三、測試用例集TestSuite

        完整的單元測試很少只執行一個測試用例,開發人員通常需要編寫多個測試用例才能對某一軟體功能進行比較完成的測試,這些相關的測試用例稱為一個測試用例集,在PyUnit中是用TestSuite類來表示的。PyUinit測試框架允許Python程式設計師在單元測試程式碼中定義一個名為suite()的全域性函式,並將其作為整個單元測試的入口,PyUnit通過呼叫它來完成整個測試過程:
  1. def suite():  
  2.     suite = unittest.TestSuite()  
  3.     suite.addTest(WidgetTestCase("testSize"))  
  4.     suite.addTest(WidgetTestCase("testResize"))  
  5.     return suite  
        也可以直接定義一個TestSuite的子類,並在其初始化方法__init__中完成所有測試用例的新增:
  1. class WidgetTestSuite(unittest.TestSuite)  
  2.     def __init__(self):  
  3.         unittest.TestSuite.__init__(self, map(WidgetTestCase, ("testSize""testResize")))  
        這樣只需要在suite()方法中返回該類的一個例項就可以了:
  1. def suite():  
  2.     return WidgetTestSuite()  
        在PyUnit測試框架中,TestSuite類可以看成是TestCase類的一個容器,用來對多個測試用例進行組織,這樣多個測試用例可以自動在一次測試中全部完成。事實上,TestSuite除了可以包含TestCase外,也可以包含TestSuite,從而可以構成一個更龐大的測試用例集:
        suite1 = mysuite1.TheTestSuite()
        suite2 = mysuite2.TheTestSuite()
        alltests = unittest.TestSuite((suite1, suite2))

四、實施測試TestRunner

        編寫測試用例(TestCase)並將它們組織成測試用例集(TestSuite)的最終目的只有一個:實施測試並獲得最終結果。PyUnit使用TestRunner類作為測試用例的基本執行環境,來驅動整個單元測試過程。但是Python開發人員在進行單元測試時一般不直接使用TestRunner類,而是使用其子類TextTestRunner來完成測試,並將測試結果以文字方式顯示出來。舉例說明如下:
  1. #text_runner.py
  2. from widget import Widget  
  3. import unittest  
  4. #執行測試的類
  5. class WidgetTestCase(unittest.TestCase):  
  6.     def setUp(self):  
  7.         self.widget = Widget()  
  8.     def tearDown(self):  
  9.         self.widget.dispose()  
  10.         self.widget = None
  11.     def testSize(self):  
  12.         self.assertEqual(self.widget.getSize(), (4040))  
  13.     def testResize(self):  
  14.         self.widget.resize(100100)  
  15.         self.assertEqual(self.widget.getSize(), (100100))  
  16. #測試
  17. if __name__ == "__main__":  
  18.     #構造測試集            
  19.     suite = unittest.TestSuite()  
  20.     suite.addTest(WidgetTestCase("testSize"))  
  21.     suite.addTest(WidgetTestCase("testResize"))  
  22.     #執行測試
  23.     runner = unittest.TextTestRunner()  
  24.     runner.run(suite)  

            使用如下命令執行該單元測試:
           $python text_runner.py

            預設情況下,TextTestRunner將結果輸出到sys.stdout/sys.stderr上,但是如果在建立TextTestRunner類例項時將一個檔案物件傳遞給了建構函式,則輸出結果將被重定向到該檔案中。

五、大道至簡main()

        PyUnit模組中定義了一個名為main的全域性方法,使用它可以很方便地將一個單元測試模組變成可以直接執行的測試指令碼,main()方法使用TestLoader類來搜尋所有包含在該模組中的測試方法,並自動執行它們。如果Python程式設計師能夠按照約定(以test開頭)來命令所有的測試方法,那麼只需要在測試模組的最後加入如下幾行程式碼即可:
  1. if __name__ == "__main__":  
  2.     unittest.main()  
        下面是利用main()方法來進行測試的完整例子:
  1. #main_runner.py       
  2. from widget import Widget  
  3. import unittest  
  4. #執行測試的類
  5. class WidgetTestCase(unittest.TestCase):  
  6.     def setUp(self):  
  7.         self.widget = Widget()  
  8.     def tearDown(self):  
  9.         self.widget.dispose()  
  10.         self.widget = None
  11.     def testSize(self):  
  12.         self.assertEqual(self.widget.getSize(), (4040))  
  13.     def testResize(self):  
  14.         self.widget.resize(100100)  
  15.         self.assertEqual(self.widget.getSize(), (100100))  
  16. #測試
  17. if __name__ == "__main__":  
  18.     unittest.main()  
        使用如下命令執行上面的單元測試:
       $python main_runner.py
        如上這樣將執行WidgetTestCase中的所有測試方法,但是如果只想執行testSize()方法,則可以如下這般:
       $python main_runner.py WidgetTestCase.testSize
        如果在單元測試指令碼中定義了TestSuite,還可以指定要執行的測試集,使用-h引數可以檢視執行該指令碼所有可能用到的引數:
       $python main_runner.py -h


        需要注意的是:PyUnit的TestCase中如果有多個test_xxx,則預設按照xxx的字母順序執行測試用例函式,如果test_xxx之間有依賴關係的話就會出錯,解決方法有二:1、解耦;2、編寫xxx函式時人為地按字母順序。
        當然,如果你安裝了Python 2.7.2及以上版本,你還可以利用discover函式來自動發現並執行測試用例:
       $python2.7 -m unittest discover