申明:本文是基於python3.x及selenium3.x.
unittest,也可以稱為PyUnit,類似於JUnit,用於python項目中,可以用來創建全面的測試套件,可以用於單元自動化測試(模塊)、接口自動化測試(接口)、功能自動化測試(UI)等等。
官方文檔:https://docs.python.org/3.6/library/unittest.html
unittest具備創建測試用例、測試套件、測試夾具的能力,包括的組件如下:
- Test Fixture(測試夾具):通過使用測試夾具,可以定義在單個或者多個測試執行之前的准備工作和測試執行后的清理工作。
- Test Case(測試用例):一個測試用例是在unittest中執行測試的最小單元。它通過unittest提供的assert方法來驗證一組特定的操作或輸入以后得到的具體響應。unittest提供了一個名為TestCase的基礎類,可以用來創建測試用例。
- Test Suite(測試套件):一個測試套件是多個測試用例的集合,是針對被測程序的對應功能和模塊創建的一組測試,一個測試套件內的測試將一起執行。
- Test Runner(測試執行器):測試執行器負責測試執行調試並且生成測試結果給用戶。測試執行器可以使用圖形界面、文本界面或者特定的返回值來展示測試執行結果。
- Test Report(測試報告):測試報告用來展示所有執行用例的成功或者失敗狀態的匯總,執行失敗的測試步驟的預期結果與實際結果,還有整體運行狀況及運行時間的匯總。注意,unittest本身是沒有相應的內置模塊來生成友好的報告,但我們可以借用unittest的擴展庫HTMLTestRunner來實現,需要單獨下載並放到python安裝目錄下。
TestCase類
我們可以通過繼承TestCase類並且在測試類為每一個測試添加測試方法來創建單個測試或者一組測試。每個測試最重要的任務是調用assert方法來比對結果,如調用assEqual()來檢驗結果,調用assertTrue()來驗證條件,或者調用assertRises()來驗證預期的異常。
除了添加測試,我們還要添加測試夾具——setUp()方法和tearDown()方法,用來創建或處置測試用用例所需的任何對象和條件。
下面就邊實操邊說明:
首先,我們需要先引入unittest模塊,然后定義一個繼承於TestCase類的子類,如下所示:
import unittest from selenium import webdriver class BaiduSearchTest(unittest.TestCase):
setUp()方法
一個測試用例是從setUp()方法開始執行的,用這個方法在每個測試執行前去執行一些初始化的任務。比如創建瀏覽器實例,訪問URL,加載測試數據和打開日志文件等。此方法沒有參數,不返回任何值。當定義一個setUp()方法,測試執行器在每次執行測試方法之前優先執行該方法。在下面的例子里,我將用setUp()方法來創建Chrome實例,並在測試開始執行之前訪問到被測程序的主頁。代碼如下:
import unittest from selenium import webdriver class BaiduSearchTest(unittest.TestCase): def setUp(self): #create a new Chrome session self.driver = webdriver.Chrome() self.driver.implicitly_wait(30) self.driver.maximize_window() self.driver.get('https://www.baidu.com')
編寫用例
有了setUp()方法,現在可以寫一些測試用例來驗證被測程序的功能。與setUp()方法相似,test方法也是在TestCase類中實現。重點是測試方法的命名必須以test開頭,這種命名約定也通知test runner哪個方法代表測試方法。對於test runner能找到的每個測試方法,都會在執行測試方法之前先執行setUp()方法。這樣做有助於確保每個測試方法能夠依賴相同的環境,無論類中有多少測試方法。下面將添加一個新測試方法test_search_python(),並使用簡單的assertEqual()方法來驗證搜索術語返回的結果和預期結果相匹配。代碼如下:
import unittest from selenium import webdriver from time import sleep class BaiduSearchTest(unittest.TestCase): def setUp(self): #create a new Chrome session self.driver = webdriver.Chrome() self.driver.implicitly_wait(30) self.driver.maximize_window() self.driver.get('https://www.baidu.com') def test_search_python(self): #get the search textbox search_textbox = self.driver.find_element_by_id('kw') search_textbox.clear() #enter search keyword search_textbox.send_keys("python") #get the and seacrh button and click search_button = self.driver.find_element_by_id('su') search_button.click() #add assert sleep(2) tag = self.driver.find_element_by_link_text("PyPI").text self.assertEqual('PyPI',tag)
tearDown()方法
類似於setUp()方法在每個測試方法之前被調用,TestCase類也會在測試執行完成后調用tearDown()方法來清理所有的初始化值。一旦測試被執行,在setUp()中定義的值將不再需要,所以最好的在完成的時候清理掉。在我的例子中,在測試執行完成后,就不再需要Chrome的實例。我將在tearDown()方法中關閉瀏覽器,代碼接上面的如下:
... def tearDown(self): #close the browser window self.driver.quit()
運行測試
為了通過命令行運行測試,我們可以在測試用例中添加調用簡單的unittest.main()方法,並將傳遞verbosity參數以便在控制台顯示詳細的測試情況。代碼接上面的如下:
... if __name__ == '__main__': unittest.main(verbosity=2)
然后將整個測試代碼保存為python腳本,在這個例子里,保存為baidusearch.py。在腳本存放路徑下用命令行窗口通過下面的命令在執行該測試。在測試運行結束后,unittest會把測試結果顯示在控制台,如下所示:
除了測試結果概要外,當一個測試用例執行失敗,針對每個失敗也會通過文本信息展示具體哪里有錯誤。如下所示:
上圖展示了具體哪個測試方法執行失敗,通過打印信息可以定位具體導致失敗的代碼。另外,失敗自身也會以AssertionError形式顯示,上面例子為預期結果與實際結果不匹配。
添加其他測試
下面將為測試類添加其它的測試。規則很簡單,新的測試方法命名也要以test開頭,代碼如下:
... def test_search_selenium(self): search_textbox = self.driver.find_element_by_id('kw') search_textbox.clear() search_textbox.send_keys("python") search_button = self.driver.find_element_by_id('su') search_button.click() sleep(2) tag = self.driver.find_element_by_link_text("Selenium Remote Control").text self.assertIn('Selenium',tag)
運行整個測試類將看到兩個Chrome的實例將打開和關閉,這正是setUp()方法和tearDown()方法針對每個測試方法都要執行產生的結果,如下圖所示:
更多基於python的unittest信息可以參考:https://docs.python.org/3.6/library/unittest.html