unittest模塊學習(四)

25.3.4 組織測試代碼

單元測試的基本構建是測試用例,必須設置和檢查每個場景的正確性。在unittest中,測試用例由unittest的TestCase類的實例表示。爲了製作屬於自己的測試用例,必須要編寫TestCase的子類,或者使用FunctionTestCase。

TestCase派生類的對象是一個可以完全運行單個測試方法的對象,以及可選的設置和整理代碼。

TestCase實例的測試代碼應該是完全自包含的,這樣他可以獨立運行或者與任意數量的其他測試用例組合運行。

最簡單的TestCase子類將簡單地重寫runTest()方法以執行特定的測試代碼。
import unittest

class DefaultWidgetSizeTestCase(unittest.TestCase):
    def runTest(self):
        widget = Widget('The widget')
        self.assertEqual(widget.size(), (50, 50), 'incorrect default size')
請注意,這裏我們爲了測試某些內容,使用了TestCase基類提供的assert*()方法。如果測試失敗,會拋出異常,然後unittest會將這個測試用例標記爲失敗。其他的異常將會被視爲錯誤。者能夠幫助你識別問題的所在:失敗是由於不正確的結果引起的,比如預期結果返回一個6而實際結果返回的是3.錯誤則是由不正確的代碼引起的。比如說ValueError、TypeError等。

運行測試用例的方法將會在之後介紹。現在我們構造一個測試用例的實例,我們調用它的構造函數而不帶參數。
testCase = DefaultWidgetSizeTestCase()
現在這樣的測試用例有很多,並且它們的設置可能是重複的。在前面的例子中,構建100個widget和構建一個widget意味着難看的複製。

幸運的是我們能夠通過實現setUp()的方法來分解這些設置代碼,當我們運行測試時,測試框架會自動調用這個方法。
import unittest

class SimpleWidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

class DefaultWidgetSizeTestCase(SimpleWidgetTestCase):
    def runTest(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

class WidgetResizeTestCase(SimpleWidgetTestCase):
    def runTest(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')
如果setUp()方法在測試運行的時候拋出異常,框架則會認爲測試發生了錯誤,然後runTest()方法將不會執行。

相似的,我們能夠使用tearDown()方法,在runTest()方法運行之後整理:
import unittest

class SimpleWidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def tearDown(self):
        self.widget.dispose()
        self.widget = None
如果setUp()方法成功,無論runTest()成功或者失敗,tearDown()方法都將在runTest()之後運行。

這種測試代碼的工作環境叫做夾具(fixture)。

通常,很多小測試用例使用相同的夾具。在這種情況下,我們最終將SimpleWidgetTestCase繼承爲許多小的單方法類,如 DefaultWidgetSizeTestCase。這是非常耗時的,所以unittest提供了一個更簡單的機制:
import unittest

class WidgetTestCase(unittest.TestCase):
    def setUp(self):
        self.widget = Widget('The widget')

    def tearDown(self):
        self.widget.dispose()
        self.widget = None

    def test_default_size(self):
        self.assertEqual(self.widget.size(), (50,50),
                         'incorrect default size')

    def test_resize(self):
        self.widget.resize(100,150)
        self.assertEqual(self.widget.size(), (100,150),
                         'wrong size after resize')
這裏我們沒有提供runTest()方法,但是新增了兩個不同的測試方法。類實例現在將會運行一個test_*()方法,並且爲每個實例分別創建和銷燬self.widget。創建實例時,我們必須指定要運行的測試方法。我們通過在構造函數中傳遞方法名來完成此操作。
defaultSizeTestCase = WidgetTestCase('test_default_size')
resizeTestCase = WidgetTestCase('test_resize')
測試用例實例根據測試的功能分組在一起。unittest爲此提供了一種機制:由unittest的TestSuite類表示的測試套件:
widgetTestSuite = unittest.TestSuite()
widgetTestSuite.addTest(WidgetTestCase('test_default_size'))
widgetTestSuite.addTest(WidgetTestCase('test_resize'))
爲了便於運行測試,我們將在後面看到,最好在每個測試模塊提供一個返回預構建測試套件的可調用對象:
def suite():
    suite = unittest.TestSuite()
    suite.addTest(WidgetTestCase('test_default_size'))
    suite.addTest(WidgetTestCase('test_resize'))
    return suite
或者
def suite():
    tests = ['test_default_size', 'test_resize']

    return unittest.TestSuite(map(WidgetTestCase, tests))
由於使用許多類似命名的測試函數創建TestCase子類是一種常見模式,因此unittest提供了一個TestLoader類,可用於自動創建測試套件並使用單個測試填充測試套件。例如:
suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase)
將創建一個將運行WidgetTestCase.test_default_size()和WidgetTestCase.test_resize的測試套件。TestLoader使用'test'方法名稱前綴自動識別測試方法。

請注意,各種測試用例的運行順序是通過對測試函數名稱與字符串的內置排序進行排序來確定的。
通常希望將測試用例集合在一起,以便依次運行整個系統的測試。這很容易,因爲TestCase實例可以添加到TestSuite一樣:
suite1 = module1.TheTestSuite()
suite2 = module2.TheTestSuite()
alltests = unittest.TestSuite([suite1, suite2])
您可以將測試用例和測試套件的定義放置在與測試代碼相同的模塊中,但將測試代碼放置在單獨的模塊中有幾個優點,例如test_widget.py:
-測試模塊可以從命令行單獨運行。
-測試代碼可以更加容易的從提供的代碼中分離出來。
-測試代碼應該比它測試的代碼更加頻繁地被修改。
-測試過的代碼可以更容易的重構。
-用C編寫的模塊測試必須在不同的模塊中,爲什麼不統一呢?
-如果測試策略發生變化,則不需要更改源代碼。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章