- 什么是單元測試?
單元測試是對最小的軟件設計單元(模塊、類)進行驗證,它使用開發文檔中對模塊的描述作為指南,對重要的程序分支進行測試以發現模塊中的錯誤。
- 單元測試框架可以解決說明問題?
1、提供用例組織與執行
2、提供豐富的斷言方法
3、提供豐富的日志
- 重要概念
1.Test Case 測試用例
繼承unittest.TestCase的子類里的每個名為test*的函數都被視為單個測試用例,前后執行setUp()和tearDown()
2.Test Suite 測試用例集合——把多個測試用例集合在一起執行,可以通過addTest加載TestCase到TestSuite中。
-單元測試的加載方式有2種:一種是通過unittest.main()來啟動單元測試的測試模塊(文件);1.被測試類繼承了unittest.TestCase;2.被測試的函數名為test*
-一種是添加到testsuite集合中再加載所有的被測試對象,而testsuit里存放的就是單元測試的用例。
(另一種TestLoader類的dicover方法直接加載文件中所有被測試對象,其實也應用了testsuite原理)
1 # -*-coding:utf-8-*- 2 3 import unittest 4 5 def setUpModule(): # 模塊的前后執行 6 print("測試模塊開始-------") 7 def tearDownModule(): 8 print("測試模塊結束=======") 9 10 class Test(unittest.TestCase): 11 12 @classmethod 13 def setUpClass(cls): # 類的前后執行 14 print("測試類開始------") 15 @classmethod 16 def tearDownClass(cls): 17 print("測試類結束=======") 18 19 def setUp(self): # 函數的前后執行 20 print("測試用例開始------") 21 def tearDown(self): 22 print("測試用例結束======") 23 def test_case(self): 24 print("我是用例") 25 26 unittest.main()
3.Test Runner 執行
通過TextTestRunner類提供的run()方法來執行test suite/test case,test runner可以使用圖形界面、文本界面,或返回一個特殊的值方法來表示測試執行的結果。
runner = unittest.TextTestRunner()
runner.run(xxx)
4.Test Fixture 執行前后的操作setUp()和tearDown()
1 # -*-coding:utf-8-*- 2 3 import unittest 4 5 def setUpModule(): 6 print("測試模塊開始-------") 7 def tearDownModule(): 8 print("測試模塊結束=======") 9 10 class Test(unittest.TestCase): 11 12 @classmethod 13 def setUpClass(cls): 14 print("測試類開始------") 15 @classmethod 16 def tearDownClass(cls): 17 print("測試類結束=======") 18 19 def setUp(self): 20 print("測試用例開始------") 21 def tearDown(self): 22 print("測試用例結束======") 23 def test_case(self): 24 print("我是用例") 25 26 unittest.main()
- 斷言方法
unittest的TestCase類提供下面方法用於測試結果的判斷:
| 斷言語法 | 解釋 |
| assertEqual(a, b) | 判斷a==b |
| assertNotEqual(a, b) | 判斷a!=b |
| assertTrue(x) | bool(x) is True |
| assertFalse(x) | bool(x) is False |
| assertIs(a, b) | a is b |
| assertIsNot(a, b) | a is not b |
| assertIsNone(x) | x is None |
| assertIsNotNone(x) | x is not None |
| assertIn(a, b) | a in b |
| assertNotIn(a, b) | a not in b |
| assertIsInstance(a, b) | isinstance(a, b) 斷言a是b的一個實例 |
| assertNotIsInstance(a, b) | not isinstance(a, b) 斷言a不是b的實例 |
- assertEqual(a , b , msg=None)
斷言第一個參數a和第二個參數b是否相等,如果不等則測試失敗Fail。msg為可選參數,用於定義測試失敗時打印的信息。
- TestLoader類
discover以py文件(模塊)化方式一次執行模塊里所有名為test*的函數。替代了suite.addTest()。
正常情況下,不需要創建這個類的實例。unittest提供了可以共享的defaultTestLoader類,可以直接使用。
- unittest.defaultTestLoad.discover(star_dir, pattern='test*.py', top_level_dir=None)
- 注意:每個文件夾中必須創建__init__.py文件,discover才能識別文件夾里名為test*.py的文件。
1 import unittest 2 3 # discover()方法會匹配指定當前目錄下./的所有test*.py的用例文件,並將查找到的測試用例組裝到測試套件賦在discover中 4 5 test_dir = './' 6 discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py') 7 8 # 直接通過run()方法執行 9 10 if __name__ == '__main__': 11 runner = unittest.TextTestRunner() 12 runner.run(discover)
- 用例執行順序
unnitest框架默認根據同級目錄下模塊或文件名的ASCII碼的順序來加載測試用例,所以只能通過命名來改變執行順序。除非用TestSuite類的addTest()方法按照別的順序加載。
文件 > 類 >函數
A~Z,a~z,0~9,
例:(當用discover時)
aaa文件夾-testsub.py
testadd.py
-aaa文件與testadd.py同級,但aaa的ASCII碼<testadd的ASCII碼,所以aaa文件夾-testsub.py要優先於testadd.py!
- unittest如何識別多層目錄?
在每個目錄加上__init__.py文件,就可以識別當前目錄以下的所有文件——一個包是一個帶有特殊文件 __init__.py 的目錄。__init__.py 文件定義了包的屬性和方法。其實它可以什么也不定義;可以只是一個空文件,但是必須存在。如果 __init__.py 不存在,這個目錄就僅僅是一個目錄,而不是一個包,它就不能被導入或者包含其它的模塊和嵌套包。
Python中__init__.py文件的作用詳解 http://www.jb51.net/article/92863.htm
- 跳過測試和預期失敗
1 # -*-coding:utf-8-*- 2 import unittest 3 4 class MyTest(unittest.TestCase): 5 @unittest.skip("必輸入reason") # 無條件跳過測試,說明原因 6 def test_skip1(self): 7 print('直接跳過測試~') 8 @unittest.skipIf(3>2,"必輸入reason") # 如果條件為真,跳過測試 9 def test_skip2_if(self): 10 print('條件為真,則跳過測試~') 11 @unittest.skipUnless(3>2,"必輸入reason") # 如果條件為假,跳過測試 12 def test_skip3_unless(self): 13 print('條件為假,則跳過測試') 14 @unittest.expectedFailure # 不管結果如何,不拋錯 15 def test_excepted_failure(self): 16 self.assertEqual(2, 2) 17 18 19 unittest.main()
- 保存測試結果報告
打開cmd,到runtest.py模塊的當前目錄,新建文件夾report,輸入:
> python runtest.py >> report/log.txt 2>&1
發現生成了一個report文件夾里的log.txt為結果報告。
