摘抄自:
一、基礎概念
unittest官方文檔:https://docs.python.org/3.5/library/unittest.html

- TestCase:一個TestCase的實例就是一個測試用例。什么是測試用例呢?就是一個完整的測試流程,包括測試前准備環境的搭建(setUp),執行測試代碼(run),以及測試后環境的還原(tearDown)。元測試(unit test)的本質也就在這里,一個測試用例是一個完整的測試單元,通過運行這個測試單元,可以對某一個問題進行驗證。
- TestSuite:而多個測試用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
- TestLoader:是用來加載TestCase到TestSuite中的,其中有幾個loadTestsFrom__()方法,就是從各個地方尋找TestCase,創建它們的實例,然后add到TestSuite中,再返回一個TestSuite實例。
- TextTestRunner:是來執行測試用例的,其中的run(test)會執行TestSuite/TestCase中的run(result)方法。
- TextTestResult:測試的結果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息
那么整個流程就是:首先是要寫好TestCase,然后由TestLoader加載TestCase到TestSuite,然后由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,整個過程集成在unittest.main模塊中
二、測試案例分析
1、示例1
# 這是unittest文檔上的例子 import random import unittest class TestSequenceFunctions(unittest.TestCase): def setUp(self): self.seq = range(10) def test_shuffle(self): # make sure the shuffled sequence does not lose any elements random.shuffle(self.seq) self.seq.sort() self.assertEqual(self.seq, range(10)) # should raise an exception for an immutable sequence self.assertRaises(TypeError, random.shuffle, (1,2,3)) def test_choice(self): element = random.choice(self.seq) self.assertTrue(element in self.seq) def test_sample(self): with self.assertRaises(ValueError): random.sample(self.seq, 20) for element in random.sample(self.seq, 5): self.assertTrue(element in self.seq) if __name__ == '__main__': unittest.main()
小知識:
python assert斷言是聲明布爾值必須為真的判定,如果發生異常就說明表達式為假。 可以理解assert斷言語句為raise-if-not,用來測試表示式,其返回值為假,就會觸發異常。 self.assertEqual(a,b,msg=msg) #判斷a與.b是否一致,msg類似備注,可以為空 self.assertNotEqual(a,b,msg=msg) #判斷a與b是否不一致 self.assertTrue(a,msg=none) #判斷a是否為True self.assertFalse(b,msg=none) #判斷b是否為false
繼續:
TestSequenceFunctions繼承自unittest.TestCase,重寫了setUp()方法,並且定義了三個以'test'開頭的方法,那這個TestSequenceFunctions類到底是個什么呢?它是一個測試用例,還是三個測試用例?說是三個測試用例的話,它本身繼承自TestCase,說是一個測試用例的話,里面又有三個test_*()方法,明顯是三個測試用例。其實,我們只要看一些TestLoader是如何加載測試用例的,就一清二楚了,在loader.TestLoader類中有一個loadTestsFromTestCase()方法:
def loadTestsFromTestCase(self, testCaseClass): """Return a suite of all tests cases contained in testCaseClass""" if issubclass(testCaseClass, suite.TestSuite): raise TypeError("Test cases should not be derived from TestSuite." \ " Maybe you meant to derive from TestCase?") testCaseNames = self.getTestCaseNames(testCaseClass) if not testCaseNames and hasattr(testCaseClass, 'runTest'): testCaseNames = ['runTest'] loaded_suite = self.suiteClass(map(testCaseClass, testCaseNames)) return loaded_suite
getTestCaseNames()是從TestCase這個類中找所有以“test”開頭的方法,然后注意第9行,在構造TestSuite對象時,其參數使用了一個map方法,即對testCaseNames中的每一個元素,使用testCaseClass為其構造對象,其結果是一個TestCase的對象集合,可以用下面的代碼來分步說明:
testcases = [] for name in testCaeNames: testcases.append(TestCase(name)) loaded_suite = self.suiteClass(tuple(testcases))
可見,對每一個以test開頭的方法,都為其構建了一個TestCase對象,值得注意的是,如果沒有定義test開頭的方法,而是將測試代碼寫到了一個名為runTest的方法中,那么會為該runTest方法構建TestCase對象,如果定義了test開頭的方法,就會忽略runTest方法。
也就是說:
每一個以test開頭的方法,都會為其構建TestCase對象,也就是說TestSequenceFunctions類中其實定義了三個TestCase,之所以寫成這樣,是為了方便,因為這幾個測試用例的fixture是相同的,如果每一個測試用例單獨寫成一個TestCase的話,會有很多的冗余代碼。
2、示例2
前置條件(setUp)、后置條件(tearDown)和Test Suite的使用
import unittest class MyTestCase(unittest.TestCase): # 繼承unittest.TestCase @classmethod def setUpClass(cls): # 必須使用 @classmethod 裝飾器,所有test運行前運行一次 print('這是所有case的前置條件') @classmethod def tearDownClass(cls): # 必須使用 @classmethod 裝飾器, 所有test運行完后運行一次 print('這是所有case的后置條件') def setUp(self): # 每個測試用例執行之前的操作 print('這是每條case的前置條件') def tearDown(self): # 每個用例執行之后的操作 print('這是每條case的后置條件') def test_Third(self): # 測試用例的命名必須以test開頭,否則不予執行 print('03: 第三條case') def test_First(self): print('01: 第一條case') @unittest.skip('不執行這條case') # 跳過這條case def test_Second(self): print('02: 第二條case') def test_Fourth(self): print('04: 第四條case') if __name__ == '__main__': # unittest.main() # 使用main()直接運行時,將按case的名稱順序執行 suite = unittest.TestSuite() suite.addTest(MyTestCase("test_Third")) # 將需要執行的case添加到Test Suite中,沒有添加的不會被執行 suite.addTest(MyTestCase("test_Second")) suite.addTest(MyTestCase("test_First")) runner = unittest.TextTestRunner() runner.run(suite) # 將根據case添加的先后順序執行
結果
這是所有case的前置條件 這是每條case的前置條件 03: 第三條case 這是每條case的后置條件 這是每條case的前置條件 01: 第一條case 這是每條case的后置條件 這是所有case的后置條件
3、注意
如果你使用的是PyCharm,那么在使用unittest測試的時候,會發現你使用了addText,執行順序還是默認的執行順序(按照ASCII執行),這是IDE工具的問題,解決方法如下:
1、點擊pycharm的右上角下拉菜單,點擊Edit configurations 2、將Python tests里的對應文件的py.test for...或者unittest for...的文件刪除(選中后點擊左上角的減號) 3、點擊 +,在下拉菜單中選擇Python,然后在右邊的script path里...選中所要運行的文件 4、最后點擊ok即可,再在所要運行的文件處(最好是main處)點擊右鍵就會發現run unittest變成了run
三、生成測試報告(python2版本)
pip install html-testRunner
import unittest import HTMLTestRunner class MyTestCase(unittest.TestCase): # 繼承unittest.TestCase @classmethod def setUpClass(cls): # 必須使用 @classmethod 裝飾器,所有test運行前運行一次 print('這是所有case的前置條件') @classmethod def tearDownClass(cls): # 必須使用 @classmethod 裝飾器, 所有test運行完后運行一次 print('這是所有case的后置條件') def setUp(self): # 每個測試用例執行之前的操作 print('這是每條case的前置條件') def tearDown(self): # 每個用例執行之后的操作 print('這是每條case的后置條件') def test_Third(self): # 測試用例的命名必須以test開頭,否則不予執行 print('03: 第三條case') def test_First(self): print('01: 第一條case') @unittest.skip('不執行這條case') # 跳過這條case def test_Second(self): print('02: 第二條case') def test_Fourth(self): print('04: 第四條case') if __name__ == '__main__': # unittest.main() # 使用main()直接運行時,將按case的名稱順序執行 suite = unittest.TestSuite() suite.addTest(MyTestCase("test_Third")) # 將需要執行的case添加到Test Suite中,沒有添加的不會被執行 suite.addTest(MyTestCase("test_Second")) suite.addTest(MyTestCase("test_First")) f = open('res.html', 'wb') # 打開一個保存結果的html文件 runner = HTMLTestRunner.HTMLTestRunner(stream=f, title='測試報告', description='測試情況') # 生成執行用例的對象 runner.run(suite)
如果我們有很多個模塊,每個模塊下面都寫了很多python文件,每個python文件里面都有測試用例,那怎么把這個目錄下的用例都執行了呢,就要先找到這個目錄下的所有python文件,然后找到里面的測試用例,逐個執行,代碼如下:
import unittest import HTMLTestRunner suite = unittest.TestSuite() # 創建測試套件 # 找到某個目錄下所有的以test開頭的Python文件里面的測試用例 all_cases = unittest.defaultTestLoader.discover('.', 'test_*.py') for case in all_cases: suite.addTests(case) # 把所有的測試用例添加進來 fp = open('res.html', 'wb') runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title='all_tests', description='所有測試情況') runner.run(suite)
我們在后續進行持續集成的時候,要讓代碼自動運行,就會用到Jenkins了,但是上面產生的測試報告都是html格式的,Jenkins不認識,就在Jenkins里面顯示不出來。那咱們就要產生一些Jenkins認識的測試報告,Jenkins認識xml格式的報告,那咱們就產生xml格式的唄,就需要用一個新的模塊,xmlrunner,安裝直接 pip install xmlrunner即可,代碼如下:
import unittest import xmlrunner #導入這個模塊 class My(unittest.TestCase): def test1(self,a,b,c): self.assertEqual(a+b,c) if __name__=='__main__': test_suite = unittest.TestSuite() test_suite.addTest(unittest.makeSuite(My)) runner = xmlrunner.XMLTestRunner(output='report')#指定報告放的目錄 runner.run(test_suite
四、Webtest
這個模塊很重要,官網已經寫得很詳細清楚了,因此這里不再贅述,直接上網址
https://docs.pylonsproject.org/projects/webtest/en/latest/
記得去看哦
