unittest 單元測試架構
Unittest 單元測試架構簡介
Unittest 是python内置的單元測試架構,具備編寫用例、組織用例、執行用例、功能,可以結合selenium 進行UI自動化測試,也可以結合appium、requests 等子產品做其它自動化測試。
使用unittest 前需要熟悉該架構的五個概念:
test case : 一個完整的測試單元,執行該測試單元可以完成對某一個問題的驗證,完整提現在測試前環境準備(setUp),執行測試代碼(run),一級測試後環境還原(tearDown)‘
test suit :多個測試用例的集合,測試套件或測試計劃;
testLoader : 加載TestCase 到TestSuite 中的,其中loadTestsFrom__() 方法用于尋找TestCase,并建立它們的執行個體,然後添加到TestSuite中。
test runner : 執行多個測試用例,并将測試結果儲存到TestTestResults 執行個體中,包括允許多少測試用例,成功了多少,失敗了多少等資訊;
test fixture : 一個測試用例的初始化準備環境及環境還原,主要是setUp() 和setDown() 方法
Unittest 基礎架構
unittest 基礎使用步驟:
- 用import 語句引入unittest 子產品
- 讓所執行的測試的類都基礎于TestCase類,可以将TestCase看出是對特定類程序測試的方法的集合
- setUp() 方法中進行測試前的初始化工作, teardown() 方法中執行測試後的清理工作,它們都是TestCase 中的方法
- 編寫測試的方法最好以test 開頭(可以直接運作)
def test_add(self)、def test_sub(self)等,可以編寫多個測試用例對被測對象進行測試
- 在編寫測試方法過程中,使用TestCase class提供的方法測試功能點,比如:assertEqual等
- 調用 unittest.main() 方法運作所有以test開頭的方法。
unittest 代碼示例
import unittest class test_cases(unittest.TestCase): # 繼承TestCase類 def setUp(self): # 測試用例執行之前初始化 print('setUp') def tearDown(self): # 測試用例執行之後還原、清理 print('tearDown') def testcase01(self): print('execute case01') self.assertEqual(3,3) # 斷言 if __name__ == '__main__': unittest.main() |
unittest 常用斷言介紹:
斷言即進行預期結果和實際結果對比。
assertEqual(a,b) a == b # a,b 是否相等
assertNotEqual(a,b) a != b # a,b 是否不相等
assertTrue(x) bool(x) is True # x 是否為True
assertFalse(x) bool(x) is False # x 是否為False
assertIn(a,b) a in b # a是否包含 b
assertGreater(a,b) a>b # a大于b
assertGreaterEqual(a,b) a>=b # a 大于等于b
assertLess(a,b) a<b # a 小于 b
assertLessEqual(a,b) a<=b # a 小于等于b
可以在斷言最後一個參數中加入自定義的測試失敗資訊,如:
assertEqual(10,20,”10!=20 測試失敗”) 當斷言失敗時,顯示該資訊。
unittest用例執行順序:
當在一個測試類或多個測試子產品下,用例數量較多時,unittest在執行用例(test_xxx)時,并不是從上到下的順序執行,有特定的順序。
unittest架構預設根據ACSII碼的順序加載測試用例,數字與字母的順序為0~9,A~Z,a~z。
對于類來說,class TestAxx 會優先于class TestBxx被執行,對于方法來說,test_aaa()方法會優先于test_bbb()被執行。對于測試目錄與測試檔案來說,unittest同樣時按照這個規則來加載測試用例的。
Unittest 用例執行順序代碼示例:
import unittest class test_cases_01(unittest.TestCase): def setUp(self) -> None: print('setup') def tearDown(self) -> None: print('tearDown') def test_ccc(self): print('execute test_ccc') self.assertEqual(3,3) def test_aaa(self): print('execute test_aaa') self.assertEqual(3,3) if __name__ == '__main__': unittest.main() |
Unittest 控制測試用例執行順序:
如需要unittest自行控制測試方法的執行順序,可以通過如下兩種方法;
方法一:通過addTest()添加用例的順序控制用例執行
if __name__ == '__main__': suite = unittest.TestSuite() suite.addTest(test_cases_01('test_ccc')) suite.addTest(test_cases_01('test_aaa')) unittest.main(defaultTest='suite') |
方法二:順應unittest的預設執行順序,通過設定測試類或者測試方法方法名字來實作。
Unittest 忽略用例:
在執行測試腳本的時候,可能會有某幾條用例本次不想執行,但又不想删也不想注釋,unittest通過忽略部分測試用例不執行的方式,分無條件忽略和有條件忽略,通過裝飾器實作所描述的場景。提供的裝飾器如下:
@unittest.skip(reason): 強制跳轉。reason是跳轉原因
@unittest.skipIf(condition,reason):condition為True的時候跳轉
@unittest.skipUnless(condition,reason):condition為False的時候跳轉
@unittest.expectedFailure:标記該測試預期為失敗,如果該測試方法運作失敗,則該測試不算做失敗。
Unittest忽略用例示例代碼:
import unittest class test_cases_01(unittest.TestCase): def setUp(self) -> None: print('setup') def tearDown(self) -> None: print('tearDown') @unittest.skip('無條件跳過') def test_ccc(self): print('execute test_ccc') self.assertEqual(3, 3) @unittest.skipIf(True, '條件為真時跳過') def test_aaa(self): print('execute test_aaa') self.assertEqual(3, 3) @unittest.skipUnless(False, '條件為假跳過') def test_bbb(self): print('execute test_bbb') self.assertEqual(3, 3) @unittest.expectedFailure # 預期結果為斷言失敗 def test_ddd(self): print('execute test_ddd') self.assertEqual(3, 3) def test_fff(self): print('execute test_fff') self.assertEqual(3, 3) if __name__ == '__main__': suite = unittest.TestSuite() suite.addTest(test_cases_01('test_ccc')) suite.addTest(test_cases_01('test_aaa')) suite.addTest(test_cases_01('test_bbb')) suite.addTest(test_cases_01('test_ddd')) suite.addTest(test_cases_01('test_fff')) unittest.main(defaultTest='suite') |
建構測試套件
在實際項目中,随着項目進度的開展,測試類會越來越多,可是直到現在我們還隻會一個一個單獨運作測試類,這在實際項目實踐中肯定是不可行的,在unittest中可以通過測試套件來解決該問題。
測試套件(Test Suits)是由多個測試用例(Test Case)組成的,當然也可以由多個子套件組成。
在unittest中,把測試用例加載到測試套件的方法由如下方法:
方法一:
用unittest.TestSuite()執行個體化測試套件對象後,内部的addTest() 方法對測試類颞部的測試案例逐一添加:
if __name__ == '__main__': suite = unittest.TestSuite() suite.addTest(test_cases_01('test_ccc')) # 增加單個測試用例 suite.addTest(test_cases_01('test_aaa')) unittest.main(defaultTest='suite') |
方式二:
Unitttest提供一個TestLoader類用于自動建立一個測試集并把單個測試放入到測試集中。TestLoader自動運作以test開頭的測試方法。可以通過如下方法加載用例:
if __name__ == '__main__': # 增加類下的所有用例 suite01 = unittest.TestLoader().loadTestsFromTestCase('TestCase02') # 增加子產品下所有用例 suite02 = unittest.TestLoader().loadTestsFromModule('test_case_02') suite02.run() |
在導入類及子產品下的測試用例之前,如果用例實在其它子產品,需要先進行import導入操作。
方式三:
用unittest.TestSuite()執行個體化測試套件對象後,内部的addTests()方法可以把多個子測試集合進行整合到一個大的測試集合中
if __name__ == '__main__': allsuite = unittest.TestSuite() # 增加單個測試用例 testsuite01 = unittest.TestSuite() testsuite01.addTest('test_dddd') testsuite01.addTest('test_eee') testsuite02 = unittest.TestLoader().loadTestsFromModule('TestCases03') # 增加子產品下的所有用例? allsuite.addTests(testsuite01) # 把testsuite01集合的用例加載到allsuite測試集合 allsuite.addTests(testsuite02) # 把testsuite02 集合的用例加載到allsuite測試集合 unittest.main(defaultTest='allsuite') |
方式四:
當測試用例存放在多個不同目錄下,我們能用之氣那把用例加載到測試集合中的方式是不太友善,需要不斷去導入和添加用例子產品,如此可以通過discover()方法實作,實作如下:
Discover(start_dir,pattern = ‘test*.py’,top_level_dir = None)
Start_dir : 要測試的子產品名或測試用例目錄;
Pattern=’test*.py’ :表示用例檔案名的比對原子,例子中比對檔案名以“test”開頭的“.py”檔案,星号“*”表示任意多個字元。
Top_level_dir=None :測試子產品的頂層目錄,如果沒有頂層目錄,預設是None。
該方法通過從指定的開始目錄遞歸到子目錄中查找所有測試子產品,并傳回包含它們的TestSuite對象,隻要與模式比對測試檔案和可導入的子產品名稱才會被加載。
如果一個測試檔案的名稱符合pattern,會自動查找該檔案中派生自TestCase的類包含的test開頭的方法作為測試方法。
代碼示例:
import unittest,os case_path = os.path.join(os.path.dirname(__file__)) print(case_path) def get_all_cases(): discover = unittest.defaultTestLoader.discover(case_path,pattern='test*.py',top_level_dir=None) # 加載不同子產品下的用例 suit = unittest.TestSuite() suit.addTest(discover) return suit if __name__ == '__main__': suite = unittest.TestSuite() suite = get_all_cases() unittest.main(defaultTest='suite') |
Unittest 生成測試報告
測試報告為測試結果的統計即展示,是自動化測試不可或缺的一部分,利用unittest生成測試報告方式如下:
方式一:
用unittest.main()執行測試集
if __name__ == '__main__': suite = unittest.TestSuite() unittest.main() # 執行目前子產品下所有類的測試用例 unittest.main(verbosity=2) # 執行目前子產品下所有類的測試用例 unittest.main(defaultTest='suite') # 執行suite測試集合下所有的測試用例 |
這裡的verbosity是一個選項,表示測試結果的資訊複雜度,有三個值:
0 (靜默模式) :你隻能獲得總的測試用例數和總的結果,比如100個,失敗20個,成功80個
1 (預設模式) :非常類似靜默模式,隻是再每個成功的用力前面有個“.”,每個失敗的用例前面有個“F”。
2 (詳細模式) :測試結果會顯示每個測試用例的所有相關的資訊
方式二:
使用TextTestRunner 執行測試用例集,TextTestRunner有三個參數,它們都哦有靜默參數:
- Verbosity 分别三個級别:0,1,2它們輸出的測試報告詳細程度不同,2 最詳細
- Stream 關系着測試報告的位置,如果預設為None的話,測試報告會輸出到控制台
- descriptions 測試報告的描述
mport unittest,os case_path = os.path.join(os.path.dirname(__file__)) print(case_path) def get_all_cases(): discover = unittest.defaultTestLoader.discover(case_path,pattern='test*.py',top_level_dir=None) # 加載不同子產品下的用例 suit = unittest.TestSuite() suit.addTest(discover) return suit if __name__ == '__main__': # 方式一: # suite = unittest.TestSuite() # suite = get_all_cases() # unittest.main(defaultTest='suite') # 方式二: # suite = unittest.TestSuite() # unittest.main() # 執行目前子產品下所有類的測試用例 # unittest.main(verbosity=2) # 執行目前子產品下所有類的測試用例 # unittest.main(defaultTest='suite') # 執行suite測試集合下所有的測試用例 # 方式三: from sample.test_02.test_cases_01 import test_cases_01 suite = unittest.TestSuite() suite = get_all_cases() test_runner = unittest.TextTestRunner(stream=None,descriptions=None,verbosity=2) test_runner = unittest.TextTestRunner(stream=None,descriptions=None,verbosity=0) with open('test_result.txt','w',encoding='utf-8') as file: runner = unittest.TextTestRunner(stream=file, descriptions='執行用例的測試報告', verbosity=2) runner.run(suite) |
生成的txt報告:
方式三:
使用第三方HTMLRunner 執行用例集,它可以輸出網頁版本測試報告
HTMLTestRunner 是Python标準庫的unittest子產品的一個擴充,在使用該子產品之前要下載下傳HTMLTestRunner.py檔案,并将該檔案儲存在python安裝路徑下的lib檔案夾或者是項目的子包中,在python代碼中通過import HTMLTestRunner導入,即可使用。
import unittest,os case_path = os.path.join(os.path.dirname(__file__)) print(case_path) def get_all_cases(): discover = unittest.defaultTestLoader.discover(case_path,pattern='test*.py',top_level_dir=None) # 加載不同子產品下的用例 suit = unittest.TestSuite() suit.addTest(discover) return suit if __name__ == '__main__': import HTMLTestRunner suite = unittest.TestSuite() suite = get_all_cases() html_obj = open('result.html','w+',encoding='utf-8') # 建立 runner = HTMLTestRunner.HTMLTestRunner(stream=html_obj, title='測試報告', description='測試報告') runner.run(suite) |
HTMLTestRunner常用參數:
stream : 配置測試報告要儲存的檔案路徑
title : 測試報告标題
description : 測試報告的描述資訊
生成的簡易的html報告:
測試用例資訊在報告中顯示:
一:在測試用例中加上注釋西悉尼,即可在html報告中展現,能更好的展示每個用例的資訊。
import unittest,os class TestCases03(unittest.TestCase): '''測試類的詳細資訊''' def setUp(self) -> None: print('setUp') def tearDown(self) -> None: print('tearDown') def test_aaa(self): '''測試用例test_aaa的詳細資訊''' print('execute test_aaa') self.assertEqual(3,3) def test_ccc(self): '''測試用例test_ccc的詳細資訊''' print('execute test_ccc') self.assertEqual(3,3) def test_dddd(self): '''測試用例test_dddd的詳細資訊''' print('execute test_ccc') self.assertEqual(3,3) def test_eee(self): '''測試用例test_eee的詳細資訊''' print('execute test_eee') self.assertEqual(3, 3) |
測試用例資訊在報告中顯示:
二:在測試方法中通過:
self._testMethodName = ‘設定測試用例的名稱’
self._testMethodDoc = ‘設定測試用例詳情’
def test_aaa(self): '''測試用例test_aaa的詳細資訊''' self._testMethodName = 'test_aaa' self._testMethodDoc = '測試用例test_aaa詳細資訊' print('execute test_aaa') self.assertEqual(3,3) |
生成的報告如下圖:
方式四:
使用第三方HTMLTest ReportCN 執行測試用例集,它可以輸出網頁版測試報告。
在使用該子產品之前下載下傳HTMLReportCN.py檔案,并将該檔案儲存在python安裝路徑下的lib檔案夾或項目的子包中,在python代碼中通過
import HTMLTestReportCN 導入,即可使用。
from sample.test_02.comm import HTMLTestReportCN suite = unittest.TestSuite() suite = get_all_cases() report_dir = './html_report/' # 測試報告路徑,必須以 / 結尾 report_path_obj = HTMLTestReportCN.ReportDirectory(report_dir) # 建立測試報告路徑對象 report_path_obj.create_dir('UI 自動化測試_') # 建立測試報告存放目錄 html_path = HTMLTestReportCN.GlobalMsg.get_value('report_path') # 擷取測試報告檔案對象 html_file = open(html_path,'wb') # 建立html測試報告 html_runner = HTMLTestReportCN.HTMLTestRunner(stream=html_file, title='UI 測試報告', tester='YangShiYu', description='20221013') html_runner.run(suite) |
生成的測試報告展示如下: