[Python unittest] 3-Organizing test code
阿新 • • 發佈:2017-08-30
沒有 import 註意 代碼 correct 執行 函數 true stl
-
組織測試代碼
前面已經了解到測試的原理和步驟,但只是默認類string的測試,如果是我們自己寫的類改怎麽測試呢?
如下
class Widget(object): def __init__(self,name,width=50,height=50): self.name = name self.width = width self.height = height def __repr__(self): return "Widget({0})".format(self.name) # 返回大小 def size(self): return (self.width, self.height) #重設大小 def resize(self, *args): try: self.width = args[0] self.height = args[1] except: pass return self.size() #其他方法 def dispose(self): print ‘這裏是tearDown‘ pass
測試用例可以這樣寫
import unittest class DefaultWidgetSizeTestCase(unittest.TestCase): def runTest(self): widget = Widget(‘The widget‘) self.assertEqual(widget.size(), (50, 50), ‘incorrect default size‘)
結果
python -m unittest users.tests.DefaultWidgetSizeTestCase . ---------------------------------------------------------------------- Ran 1 test in 0.000s OK
實際情況中,如果對該類我們有一百個測試用例需要寫,難道要寫100次widget = Widget(‘The widget‘)嗎?我們猿類的服務宗旨是什麽?從不寫重復的代碼
所以unittest給我們提供了setup,setup是每一個測試用例runTest之前都會執行的函數,相當於給我們提供了一個準備環境的方法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‘)python -m unittest users.tests .. ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
兩個有 runTest 的測試用例都自動執行了父類的setup函數,當setup出錯後,測試用例就不會執行了
相似的,unittest提供了在測試用例執行之後可以自動執行的方法tearDown,可以讓我們做一些自己需要的事
class SimpleWidgetTestCase(unittest.TestCase): def setUp(self): self.widget = Widget(‘The widget‘) def tearDown(self): self.widget.dispose() self.widget = None
python -m unittest users.tests 這裏是tearDown .這裏是tearDown . ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
只要setup成功,不論測試用例是否正確,tearDown都會執行
現在每一個測試用例都要繼承相同的測試夾具,這讓人感覺很不舒服,unittest提供了如下辦法,很像java的JUnitclass 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‘)
python -m unittest users.tests.WidgetTestCase 這裏是tearDown .這裏是tearDown . ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
這裏沒有runTest函數,而是用test_開頭的方法代替,每一個test_開頭的方法都會被當作一個測試獨立運行,包括setup和tearDown也是獨立的
如果只想運行部分測試用例改怎麽辦呢?if __name__ == ‘__main__‘: widgetTestSuite = unittest.TestSuite() widgetTestSuite.addTest(WidgetTestCase(‘test_default_size‘)) widgetTestSuite.addTest(WidgetTestCase(‘test_resize‘)) #suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase) unittest.TextTestRunner(verbosity=2).run(widgetTestSuite)
將你想運行的測試用例加入TestSuite
python */users/tests.py test_default_size (__main__.WidgetTestCase) ... 這裏是tearDown ok test_resize (__main__.WidgetTestCase) ... 這裏是tearDown ok ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
或者將你想測試的類加載為suite,可以測試整個類的所有用例
if __name__ == ‘__main__‘: #widgetTestSuite = unittest.TestSuite() #widgetTestSuite.addTest(WidgetTestCase(‘test_default_size‘)) #widgetTestSuite.addTest(WidgetTestCase(‘test_resize‘)) suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase) unittest.TextTestRunner(verbosity=2).run(suite)
python */users/tests.py test_default_size (__main__.WidgetTestCase) ... 這裏是tearDown ok test_resize (__main__.WidgetTestCase) ... 這裏是tearDown ok ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
更美觀的做法是
if __name__ == ‘__main__‘: def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase(‘test_default_size‘)) suite.addTest(WidgetTestCase(‘test_resize‘)) return suite def suite_map(): tests = [‘test_default_size‘, ‘test_resize‘] return unittest.TestSuite(map(WidgetTestCase, tests)) unittest.TextTestRunner(verbosity=2).run(suite())
python */users/tests.py test_default_size (__main__.WidgetTestCase) ... 這裏是tearDown ok test_resize (__main__.WidgetTestCase) ... 這裏是tearDown ok ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
有時候需要阻止各個測試用例,很簡單測試套件TestSuite像TestCase一樣被加入TestSuite
if __name__ == ‘__main__‘: def suite(): suite = unittest.TestSuite() suite.addTest(WidgetTestCase(‘test_default_size‘)) suite.addTest(WidgetTestCase(‘test_resize‘)) return suite def suite_map(): tests = [‘test_default_size‘, ‘test_resize‘] return unittest.TestSuite(map(WidgetTestCase, tests)) alltest = unittest.TestSuite([suite(), suite_map()]) unittest.TextTestRunner(verbosity=2).run(alltest)
python */users/tests.py test_default_size (__main__.WidgetTestCase) ... 這裏是tearDown ok test_resize (__main__.WidgetTestCase) ... 這裏是tearDown ok test_default_size (__main__.WidgetTestCase) ... 這裏是tearDown ok test_resize (__main__.WidgetTestCase) ... 這裏是tearDown ok ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK
測試代碼可以放在任何地方,不過有幾個原則需要註意:
- 測試模塊可以獨立在命令行執行
- 測試代碼可以很容易的從項目中分離出來
- 沒有合理的理由不要改變測試代碼來適應它所測試的代碼
- 代碼的修改應該比測試代碼的修改頻繁的多
- 被測試代碼重構更容易
- 用C編寫的模塊的測試無論如何必須是獨立的模塊,那麽為什麽不一致呢?
- 測試策略發生變化,不需要更改源代碼
[Python unittest] 3-Organizing test code