unittest單元測試框架入門及應用
一、簡介
unittest是Python單元測試框架。unittest它支援自動化測試,在測試中使用setup(初始化)和shutdown(關閉銷毀)操作,組織測試 用例為套件(批量運行),以及把測試和報告獨立開來。
測試腳手架(test fixture):為了開展一項或多項測試所需要進行的準備工作,以及所有相關的清理操作。
測試用例(test case):一個測試用例是一個獨立的測試單元。檢查輸入特定的數據時的響應。 unittest 提供一個基類: TestCase ,用於新建測試用例。
測試套件(test suite):一系列的測試用例。用于歸檔需要一起執行的測試用例。
測試運行器(test runner):一個用於執行和輸出測試結果的組件
綜上,整個流程就是首先要寫好TestCase,然後由TestLoader載入TestCase到TestSuite,然後由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,整個過程集成在unittest.main模組中。
官方文檔://docs.python.org/zh-cn/3.7/library/unittest.html
二、unittest類的屬性
unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。
unittest.main():使用她可以方便的將一個單元測試模組變為可直接運行的測試腳本,main()方法使用TestLoader類來搜索所有包含在該模組中以「test」命名開頭的測試方法,並自動執行他們。執行方法的默認順序是:根據ASCII碼的順序載入測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以以A開頭的測試用例方法會優先執行,以a開頭會後執行。
unittest.TestSuite():unittest框架的TestSuite()類是用來創建測試套件的。
unittest.TextTextRunner():unittest框架的TextTextRunner()類,通過該類下面的run()方法來運行suite所組裝的測試用例,入參為suite測試套件。
unittest.defaultTestLoader(): defaultTestLoader()類,通過該類下面的discover()方法可自動更具測試目錄start_dir匹配查找測試用例文件(test*.py),並將查找到的測試用例組裝到測試套件,因此可以直接通過run()方法執行discover。
unittest.skip():裝飾器,當運行用例時,有些用例可能不想執行等,可用裝飾器暫時屏蔽該條測試用例。一種常見的用法就是比如說想調試某一個測試用例,想先屏蔽其他用例就可以用裝飾器屏蔽。
三、跳過測試與預計的失敗
@unittest.skip(reason)
跳過被此裝飾器裝飾的測試。 reason 為測試被跳過的原因。
@unittest.skipIf(condition, reason)
當 condition 為真時,跳過被裝飾的測試。
@unittest.skipUnless(condition, reason)
跳過被裝飾的測試,除非 condition 為真。
@unittest.expectedFailure
把測試標記為預計失敗。如果測試不通過,會被認為測試成功;如果測試通過了,則被認為是測試失敗。
四、常用的斷言方法
五、unittest實例
我們就來上手,舉個簡單的例子來看看unittest裡面的屬性、函數與用例如何運行的。
1 # 導入unittest模組 2 import unittest 3 4 # unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。 5 class TestStringCase(unittest.TestCase): 6 # TestCase基類方法,所有case執行之前自動執行 7 @classmethod 8 def setUpClass(self): 9 print("這裡是所有測試用例前的準備工作") 10 11 # TestCase基類方法,所有case執行之後自動執行 12 @classmethod 13 def tearDownClass(self): 14 print("這裡是所有測試用例後的清理工作") 15 16 # TestCase基類方法,每次執行case前自動執行 17 def setUp(self): 18 print("這裡是一個測試用例前的準備工作") 19 20 # TestCase基類方法,每次執行case後自動執行 21 def tearDown(self): 22 print("這裡是一個測試用例後的清理工作") 23 24 @unittest.skip("我想臨時跳過這個測試用例.") 25 # 測試用例1 26 def test_case1(self): 27 print('執行測試用例1') 28 s = 'hello' 29 self.assertEqual(s, "hello") 30 31 # 測試用例2 32 def test_case2(self): 33 print('執行測試用例2') 34 s = '測試' 35 self.assertEqual(s, '測試') 36 37 # 測試用例3 38 def test_case3(self): 39 print('執行測試用例3') 40 s= 'ceshi' 41 self.assertEqual(s, 'ceshi') 42 43 #測試用例集合方法 44 def suite(): 45 #實例化測試套件 46 suite = unittest.TestSuite() 47 #將用例加到用例集及運行順序 48 suite.addTest(TestStringCase('test_case3')) 49 suite.addTest(TestStringCase('test_case2')) 50 return suite 51 52 #path= './' 定義測試集所在文件夾 53 #pattern='unittest_Two.py' 規定測試集文件 54 #discover方法找到path 目錄下所有文件到的測試用例組裝到測試套件 55 def discover(): 56 path = './' 57 rundiscover = unittest.defaultTestLoader.discover(path,pattern='unittest_Two.py') 58 return rundiscover 59 60 if __name__ == "__main__": 61 #運行用例方法一: 62 unittest.main() 63 64 #運行用例方法二: 65 #實例化TextTestRunner類 66 #runner = unittest.TextTestRunner() 67 #runner.run(suite()) 68 69 #運行用例方法三: 70 #實例化TextTestRunner類 71 #runner = unittest.TextTestRunner() 72 #run()方法執行discover 73 #runner.run(discover())
unittest實例方法一運行的結果:
列印中前面的實心點,表示用例斷言成功並通過,s則表示跳轉執行用例。
unittest實例方法二運行的結果:
unittest實例方法三運行的結果同方法一結果所示。
六、生成測試報告
知道了用例如何編寫,那麼我們來看看unittest的測試報告,這裡我介紹的是經大神改進後的HTMLTestRunnerCN,由於HTMLTestRunner樣式簡陋,我們不做介紹。
Github下載HTMLTestRunnerCN://github.com/findyou/HTMLTestRunnerCN
我用的是python3.x,注意下載後用3.x下面的HTMLTestRunnerCN。Git上的文件名叫HTMLTestReportCN.py。
下載文件然後存放到你的Python安裝目錄中:\Lib\site-packages目錄下即可。程式碼中如何使用呢?在上面用例的基礎上,修改為下面的程式碼。如下所示。
1 if __name__ == "__main__": 2 filePath ='D:\\Report.html' #確定生成報告的路徑 3 fp = open(filePath,'wb') 4 runner = HTMLTestReportCN.HTMLTestRunner( 5 stream=fp, 6 title='自動化測試報告', 7 #description='測試用例結果描述', #不傳默認為空 8 tester='wuwei' #測試人員名字 9 ) 10 runner.run(discover())
正常運行py用例文件,python XX.py,系統會在D盤下自動創建一個文件名為Report.html的html測試報告。上述實例運行的測試報告結果如下圖所示。
備註:剛開始應用測試報告時,不知道有沒有小夥伴發現。用例列表中的失敗與通過的按鈕展示都是紅色,但是我想成功為綠色,失敗為紅色。我這裡稍微修改了一下HTMLTestRunnerCN.py的源碼。
原程式碼:
1 tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL
修改為:
1 tmpl = (n == 0 and self.REPORT_TEST_NO_OUTPUT_TMPL or self.REPORT_TEST_WITH_OUTPUT_TMPL)
找至成功按鈕展示列表邏輯。
修改為:
1 # 通過 的樣式,加標籤效果 2 REPORT_TEST_NO_OUTPUT_TMPL = r""" 3 <tr id='%(tid)s' class='%(Class)s'> 4 <td class='%(style)s'><div class='testcase'>%(desc)s</div></td> 5 <td colspan='5' align='center'> 6 <button id='btn_%(tid)s' type="button" class="btn btn-success btn-xs" data-toggle="collapse" data-target='#div_%(tid)s'>%(status)s</button> 7 <div id='div_%(tid)s' class="collapse in"> 8 <pre> 9 %(script)s 10 </pre> 11 </div> 12 </td> 13 </tr> 14 """ # variables: (tid, Class, style, desc, status)
大家可以根據自己的需求來修改HTMLTestRunnerCN.py的源碼,來實現自己的需要的測試報告樣式與需求。
七、requests介面測試應用unittest實例
上面的Demo實例不過癮,我們來看看python requests介面測試如何來應用unittest實例的。下面我列舉的是一個慕課網的搜索介面,普通的Get請求的實例,涉及應用到介面調用,上面說明兩個簡單的用例,斷言及上述HTMLTestReportCN生成的測試報告。
1 # 導入os、unittest、requests、HTMLTestReportCN模組 2 import requests 3 import unittest 4 import os 5 import HTMLTestReportCN 6 7 # 發送Get請求方法 8 def sendGet(url, paramData): 9 result = requests.get(url=url, params=paramData).json() 10 return result 11 12 # unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。 13 class TestRequestOne(unittest.TestCase): 14 # 每次case執行前,調用介面地址 15 def setUp(self): 16 print("這裡是一個測試用例前的準備工作") 17 #介面地址 18 self.url = '//www.imooc.com/search/history' 19 20 def tearDown(self): 21 print("這裡是一個測試用例後的清理工作") 22 23 # 測試用例1 24 def test_api1(self): 25 resultData = sendGet(url=self.url, paramData={'words': 'test'}) 26 # 用例斷言 27 self.assertEqual(resultData['data'][0]['word'], "testng") 28 29 # 測試用例2 30 def test_api2(self): 31 resultData = sendGet(url=self.url, paramData={'words': '測試'}) 32 # 用例斷言 33 self.assertEqual(resultData['data'][0]['word'], "測試你好") 34 35 #測試用例集合方法 36 def suite(): 37 #實例化測試套件 38 suite = unittest.TestSuite() 39 #將用例加到用例集及運行順序 40 suite.addTest(TestRequestOne('test_api2')) 41 suite.addTest(TestRequestOne('test_api1')) 42 return suite 43 44 if __name__ == "__main__": 45 filePath = 'D:\\Report.html' # 確定生成報告的路徑 46 fp = open(filePath, 'wb') 47 runner = HTMLTestReportCN.HTMLTestRunner( 48 stream=fp, 49 title='簡單介面自動化測試報告', 50 description='簡單介面自動化測試用例結果', #不傳默認為空 51 tester='wuwei' # 測試人員名字,不傳默認為QA 52 ) 53 runner.run(suite())
上述測試用例運行結果的測試報告如下:
八、seleniumWeb自動化應用unittest實例
上面我們用unittest進行了介面測試的實例應用,下面我們就來看看unittest如何在seleniumWeb自動化中應用的。我們以百度首頁為例來進行web自動化測試,搜索並進行斷言,生成對應的測試報告。
1 # 導入os、unittest、requests、HTMLTestReportCN模組 2 from selenium import webdriver 3 import unittest 4 import os 5 import HTMLTestReportCN 6 import time 7 8 # unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。 9 class TestSeleniumOne(unittest.TestCase): 10 # 每次case執行前,調用介面地址 11 def setUp(self): 12 print("這裡是一個測試用例前的準備工作") 13 #chrome實例,打開瀏覽器網頁 14 self.driver = webdriver.Chrome() 15 self.driver.get('//www.baidu.com') 16 17 def tearDown(self): 18 print("這裡是一個測試用例後的清理工作") 19 #每個用例執行後關閉瀏覽器 20 self.driver.quit() 21 22 # 測試用例1 23 def test_selenium1(self): 24 # 利用find_element_by_id定位元素位置並模擬按鍵輸入「部落格園」,點擊百度一下按鈕搜索,斷言 25 self.driver.find_element_by_id("kw").send_keys("部落格園") 26 self.driver.find_element_by_id("su").click() 27 time.sleep(5) 28 resultTest = self.driver.find_element_by_xpath("//*[@id='1']/h3").text 29 self.assertEqual(resultTest,"部落格園 - 開發者的網上家園官方") 30 31 # 測試用例2 32 def test_selenium2(self): 33 # 利用find_element_by_id定位元素位置並模擬按鍵輸入「測試」,點擊百度一下按鈕搜索,斷言 34 self.driver.find_element_by_id("kw").send_keys("測試") 35 self.driver.find_element_by_id("su").click() 36 time.sleep(5) 37 resultTest = self.driver.find_element_by_xpath("//*[@id='3001']/div[1]/h3").text 38 self.assertEqual(resultTest,"ceshi_前景如何?博為峰16年專註軟體測試培訓") 39 40 #測試用例集合方法 41 def suite(): 42 #實例化測試套件 43 suite = unittest.TestSuite() 44 #將用例加到用例集及運行順序 45 suite.addTest(TestSeleniumOne('test_selenium1')) 46 suite.addTest(TestSeleniumOne('test_selenium2')) 47 return suite 48 49 if __name__ == "__main__": 50 filePath = 'D:\\Report.html' # 確定生成報告的路徑 51 fp = open(filePath, 'wb') 52 runner = HTMLTestReportCN.HTMLTestRunner( 53 stream=fp, 54 title='簡單Web自動化測試報告', 55 description='簡單Web自動化測試用例結果', #不傳默認為空 56 tester='wuwei' # 測試人員名字,不傳默認為QA 57 ) 58 runner.run(suite())
上述測試用例運行結果的測試報告如下: