unittest簡介
Unittest是python內置的一個單元測試框架,主要用於自動化測試用例的開發與執行
簡單的使用如下
import unittest class TestStringMethods(unittest.TestCase): def setUp(self): print("test start") def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def tearDown(self): print("test end") if __name__ == '__main__': unittest.main()
- 1.導入unittest庫
- 2.創建類繼承TestCase類
- 3.以test開頭的方法,就是實際執行的獨立用例,必須要以test開頭,因為是unittest中約定的
- 4.setUp()方法用於測試用例執行前的初始化工作,tearDown()方法用於用例執行完后的清理操作,這里用例指以test開頭的方法,也就是每個test開頭的方法執行前后都會調用這兩個方法
- 5.assertEqual等是TestCase類斷言的方法,實際就是簡單的比較並拋出異常
- 6.main()方法提供了一個測試腳本的命令行接口,可以在腳本內直接運行
運行測試
1.使用命令行python -m unittest xxx腳本名 2.有unittest.main()就直接執行腳本 結果 ---------------------------------------------------------------------- test start test end .test start test end . ---------------------------------------------------------------------- Ran 2 tests in 0.001s OK
主要結構
整體結構:unittest庫提供了Test Case, Test Suite, Test Runner, Test Fixture
- Test Case:通過繼承TestCase類,創建一個測試用例集,但這個測試用例集里面可能包含多個測試用例(或者測試步驟)即test開頭的方法
- Test Suite:把多個測試用例集合在一起來執行。可以通過addTest加載TestCase到Test Suite中,從而返回一個TestSuite實例。
- Test Runner:Test Runner是一個用於執行和輸出測試結果的組件,可以使用圖形界面,文本界面,或者返回一個特殊的值的方式來表示測試執行的結果。
- Test Fixture:提供一些腳手架類的方法,常用於測試環境的設置與清理。
構建用例
構建用例的方法主要就是繼承TestCase類,創建自己的測試類,然后用約定的test開頭命名方法,這些方法就是測試用例
class TestStringMethods(unittest.TestCase): #用例以test開頭 def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper())
寫用例的時候常使用斷言,主要斷言有以下:
斷言方法 | 檢查條件 |
---|---|
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 |
assertlsInstance(a, b) | isinstance(a, b) |
assertNotIsInstance(a, b) | not isinstance(a, b) |
還有判斷數據類型的斷言:
斷言方法 | 用於比較的類型 |
---|---|
assertMultiLineEqual(a, b) | 字符串(string) |
assertSequenceEqual(a, b) | 序列(sequence) |
assertListEqual(a, b) | 列表(list) |
assertTupleEqual(a, b) | 元組(tuple) |
assertSetEqual(a, b) | 集合(set 或 frozenset) |
assertDictEqual(a, b) | 字典(dict) |
官網還給了剩下其他的斷言,比如異常,日志等,可以查看https://docs.python.org/zh-cn/3/library/unittest.html
完善用例
1.用例環境清理
每個用例執行的時候需要獨特的測試環境,可以在單獨test方法中編寫,但是每個用例執行前后的環境清理或統一的預處理,需要特殊的Test Fixture方法解決
主要使用這兩種方法
- setUp():程序會在運行每個測試用例(以 test_ 開頭的方法)之前自動執行 setUp() 方法,該方法拋出的異常都視為error,而不是測試不通過。
- tearDown():每個測試用例(以 test_ 開頭的方法)運行完成之后自動執行 tearDown() 方法,該方法拋出的異常都視為error,而不是測試不通過,且無論用例是否出錯都會調用。
class TestStringMethods(unittest.TestCase): def setUp(self): print("test start") def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) def tearDown(self): print("test end")
2.用例類的環境清理
上面說的是每個測試用例(以 test_ 開頭的方法)的環境清理,那么每個測試類(繼承TestCase 的類)運行的時候怎么清理環境呢?
主要使用下面兩個類方法
- setUpClass():一個類方法在單個類測試之前運行。setUpClass作為唯一的參數被調用時,必須使用classmethod()作為裝飾器
- tearDownClass():一個類方法在單個類測試之后運行。setUpClass作為唯一的參數被調用時,必須使用classmethod()作為裝飾器
class TestStringMethods(unittest.TestCase): @classmethod def setUpClass(self): print("test start") def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) @classmethod def tearDownClass(self): print("test end")
3.模塊級的環境清理
運行多個測試的時候,可能會將一部分功能的測試類集中在一個文件中,對這一個文件級的環境清理主要使用下面兩種方法
-
setUpModule():模塊開始時運行
-
tearDownModule():模塊結束時運行
./test.py def setUpModule(): print('test module start') def tearDownModule(): print("test module end") class Test1(unittest.TestCase): ... class Test2(unittest.TestCase): ...
運行用例
1.通過代碼調用測試用例
if __name__ == '__main__': unittest.main()
2.命令行執行
#運行測試文件 python -m unittest test_module #測試單個測試類 python -m unittest test_module.test_class #測試多個測試類 python -m unittest test_module.test_class test_module2.test_class2 #通配符匹配測試文件執行 python -m unittest -p test*.py #顯示詳細信息 python -m unittest -v test_module #幫助 python -m unittest -h
3.通過組織Test Suit后使用Test Runner運行Suite來運行測試
組織Suite的方法很多,下面怎么組織Suite在管理用例中會介紹
class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) if __name__ == '__main__': #構建測試集 suite = unittest.TestSuite() suite.addTest(TestStringMethods("test_upper")) #執行測試 runner = unittest.TextTestRunner() runner.run(suite)
管理用例
通過組織TestSuite可以管理多個測試用例的執行,然后使用Test Runner運行Suite來運行測試,主要用的對象有:
- TestSuit:組織測試用例的實例,支持測試用例的添加和刪除,最終將傳遞給 testRunner進行測試執行;
- TextTestRunner:進行測試用例執行的實例,其中Text的意思是以文本形式顯示測試結果。測試的結果會保存到TextTestResult實例中,包括運行了多少測試用例,成功了多少,失敗了多少等信息;
1.通過addTest()的方式,上文中有
if __name__ == '__main__': #構建測試集 suite = unittest.TestSuite() suite.addTest(TestStringMethods("test_upper")) #執行測試 runner = unittest.TextTestRunner() runner.run(suite)
2.通過TestLoader()方式組織TestSuite
class TestStringMethods(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) class TestStringMethods2(unittest.TestCase): def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) if __name__ == '__main__': #此用法可以同時測試多個類 suite1 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods) suite2 = unittest.TestLoader().loadTestsFromTestCase(TestStringMethods2) suite = unittest.TestSuite([suite1, suite2]) unittest.TextTestRunner().run(suite)
3.統一管理測試用例執行,比如測試用例達到成百上千個,可以將這些用例按照所測試的功能進行拆分,分散到不同的測試文件中,最后再創建用於執行所有測試用例的runtest.py文件
比如有下面很多測試文件
├─test
│ test1.py
│ test2.py
│ test3.py
│ test4.py
│ tmp1.py
│ tmp2.py
...
如果我們只想執行test開頭的測試文件,除了上文中的命令行命令外,我們還可以使用defaultTestLoader類提供的discover()方法來加載所有的測試用例
discover(start_dir,pattern='test*.py',top_level_dir=None)
找到指定目錄下所有測試模塊,並可遞歸查到子目錄下的測試模塊,只有匹配到文件名才能被加載。如果啟動的不是頂層目錄,那么頂層目錄必須單獨指定。
- start_dir:要測試的模塊名或測試用例目錄路徑
- pattern='test*.py':表示用例文件名的匹配原則。此處匹配文件名以“test”開頭的“.py”類型的文件,幸好“*”表示任意多個字符
- top_level_dir=None:測試模塊的頂層目錄,如果沒有頂層目錄,默認為None
注意:discover()方法中的start_dir只能加載當前目錄下的.py文件,如果加載子目錄下的.py文件,需在每個子目錄下放一個_init_.py文件。
-runtest.py import unittest test_dir = './' discover = unittest.defaultTestLoader.discover(test_dir, pattern='test*.py') if __name__ == '__main__': runner = unittest.TextTestRunner() runner.run(discover)
當然,如果用例少,也可以使用addTest()的方式一個個添加到TestSuite中
4.跳過用例和預期失敗
unittest提供了實現某些需求的裝飾器,在執行測試用例時每個裝飾前面加@符號。
- unittest.skip(reason):無條件的跳過裝飾的測試,說明跳過測試的原因
- unittest.skipIf(condition,reason):跳過裝飾的測試,如果條件為真。
- unittest.skipUnless(condition,reason):跳過裝飾的測試,除非條件為真。
- unittest.expectedFailure():測試標記為失敗,不管執行結果是否失敗,統一標記為失敗,但不會拋出錯誤信息。
class TestStringMethods(unittest.TestCase): @unittest.skip("not wht") def test_upper(self): self.assertEqual('foo'.upper(), 'FOO')
5.執行順序
unittest框架默認根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以上文測試方法test_isupper()會比test_upper()先執行
同理測試類以及測試文件也是按照這個順序執行,但如果你使用addTest()的方式添加了測試,會按照添加的順序執行
測試結果
1.console輸出結果
結果中有幾個特殊字符表示不同的意思
- . :代表測試通過。有幾個點就表示有幾個測試通過
- F:代表測試失敗,F 代表 failure。
- E:代表測試出錯,E 代表 error。
- s:代表跳過該測試,s 代表 skip。
2.HTMLTestRunner輸出測試報告
HTMLTestRunner是一個第三方庫用於替代TestRunner,用於生成可視化的報表,是python2時期的產物,現在python3需要修改其內容才能用,不過網上有改好的,可以直接用
使用就是將下載好的HTMLTestRunner.py復制到...\python35\Lib目錄下,然后下面這樣使用
class TestStringMethods(unittest.TestCase): def test_upper(self): #HTMLTestRunner可以讀取docstring類型的注釋 ''' test1 ''' self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) self.assertFalse('Foo'.isupper()) if __name__ == '__main__': testsuite = unittest.TestSuite() testsuite.addTest(TestStringMethods("test_upper")) testsuite.addTest(TestStringMethods("test_isupper")) fp = open('./result.html', 'wb') runner = HTMLTestRunner(stream=fp, title='測試報告', description='測試執行情況') runner.run(testsuite) fp.close()
3.如果想打印log到測試報告可以看我另一篇文章https://www.cnblogs.com/fengf233/p/10871055.html
參考:
https://docs.python.org/zh-cn/3/library/unittest.html