1. 程式人生 > >Selenium自動化測試-unittest單元測試框架使用

Selenium自動化測試-unittest單元測試框架使用

一、什麼是unittest

當我們寫的用例越來越多時,我們就需要考慮用例編寫的規範與組織,以便於後期的維護,而unittest正是這樣一款工具。我們這裡用一個示例來展示用unittest指令碼是什麼樣子的。藉助Selenium IDE的錄製功能,可以完成這樣的操作。錄製完成後,我們按照如下步驟,將其匯出。

錄製

然後執行匯出的py檔案,可以得到類似下面的資訊:

---------------------------------------------------
Ran 1 test in 12.801s

OK

我們開啟匯出的指令碼看一看,錄製的是在百度裡面搜尋關鍵字Selenium2:

# -*- coding: utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import Select
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import
NoAlertPresentException #匯入unittest包 import unittest, time, re #SearchTest類繼承自unittest.TestCase,表明這是一個測試案例 class SearchTest(unittest.TestCase): #setUp用於初始化工作 def setUp(self): self.driver = webdriver.Firefox() self.driver.implicitly_wait(30) self.base_url = "https://www.baidu.com/"
self.verificationErrors = [] self.accept_next_alert = True #以test開頭的是我們的測試指令碼 def test_search(self): driver = self.driver driver.get(self.base_url + "/") driver.find_element_by_id("kw").click() driver.find_element_by_id("kw").clear() driver.find_element_by_id("kw").send_keys("selenium2") driver.find_element_by_id("su").click() def is_element_present(self, how, what): try: self.driver.find_element(by=how, value=what) except NoSuchElementException as e: return False return True def is_alert_present(self): try: self.driver.switch_to_alert() except NoAlertPresentException as e: return False return True def close_alert_and_get_its_text(self): try: alert = self.driver.switch_to_alert() alert_text = alert.text if self.accept_next_alert: alert.accept() else: alert.dismiss() return alert_text finally: self.accept_next_alert = True #在每個測試方法後執行,完成清理工作 def tearDown(self): self.driver.quit() self.assertEqual([], self.verificationErrors) #整個測試過程集中在unittest的main()模組中,其預設執行以test開頭的方法 if __name__ == "__main__": unittest.main()

通過這個我們大概對unittest有個直觀的瞭解了。unittest.main():使用它可以將一個單元測試模組變為可直接執行的測試指令碼,main()方法使用TestLoader類來搜尋所有包含在該模組中以“test”命名開頭的測試方法,並自動執行。執行方法的預設順序是:根據ASCII碼的順序載入測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以以A開頭的測試用例方法會優先執行,以a開頭會後執行。

二、unittest中的概念

TestCase:
一個Testcase的例項就是一個測試用例,測試用例就是一個完整的測試流程,包括初始化setUp、執行run、測試後的還原tearDown。unittest.TestCase類,所有測試用例類繼承的基本類。此類提供了很多assert方法用於檢查比較,部分如下:

1
2

多數方法都可以見其名知其意,使用的門檻很低。

TestSuite:
對一個功能的測試往往需要多測試用例的,可以把多的測試用例集合在一起執行,這就是TestSuite的概念。常用addTest()方法將一個測試用例新增到測試套件中。

TextTestRunner:
是用來執行測試用例的,其中的run(test)用來執行TestSuite/TestCase。測試的結果會儲存在TextTestResult例項中。

TestFixture:
測試準備前要做的工作和測試執行完後要做的工作.包括setUp()和tearDown()。通過覆蓋TestCase的setUp和tearDown來實現。

知道了這幾個主要的概念,我們就可以把上面的指令碼中的最後一行unittest.main(),改為以下程式碼:

    #構造測試套件
    suite = unittest.TestSuite()
    suite.addTest(SearchTest("test_search"))
    #執行測試
    runner = unittest.TextTestRunner()
    runner.run(suite)

執行之後發現和之前用unittest.main()的結果一樣。

三、用例組織

這裡我們假設,腳本當中有多個TestCase如test_case1,test_case2…,那我們應該怎樣去控制它們的執行順序呢?

執行測試用例方案一:
直接用unittest.main() 執行,這裡它搜尋所有以test開頭的測試用例方法,按照ASCII的順序執行多個用例。

執行測試用例方案二:
先例項化測試套件,將用例載入進去,再用TextTestRunner去執行用例:

 suite=unittest.TestSuite()
 suite.addTest(Test('test_case2'))
 suite.addTest(Test('test_case1'))
 runner=unittest.TextTestRunner()
 runner.run(suite)

執行的順序是用例的載入順序,比如這裡是先執行2後執行1。

執行測試用例方案三:
在方案2中,如果我們有成百上千個用例的話,一個一個add進去,是不太現實的,那麼我們可以用defaultTestLoader來載入:

    test_dir = './'
    discover = unittest.defaultTestLoader.discover(test_dir, pattern='*test.py')
    runner = unittest.TextTestRunner()
    runner.run(discover)

這裡用./指定了當前目錄,指定了*test.py檔案,對其當中的用例進行執行,順序和方案一相同。

如果這裡指定的目錄下面有多個經pattern匹配上的.py檔案呢?呼叫discover方法,首先通過test_dir定義查詢目錄,如果檔名滿足定義的pattern,那麼我們要用for迴圈來找出所有被篩選出來的用例,並將其迴圈加到套件中,主要程式碼如下:

 for test_suite in discover:
        for test_case in test_suite:
            test_unit.addTests(test_case)

由上面組織用例的方式我們可以知道,在實際的測試用指令碼開發中,我們可以在目錄下建立xx.py的檔案,當用例穩定執行後,可以修改成test_xx.py,以便於新增到測試套件中。注意,檔名的匹配規則,我們可以隨便由pattern引數定義。

如果要執行多級目錄結構的用例呢?要想被discover讀取執行,我們要在目錄下加_ init _.py檔案

四、一個例子

下面簡單的介紹一個用unittest組織的用例結構,先建立D:\Test_Project目錄,下面放上test_case和test_report來分別存放用例和報告。

1. 編寫測試用例
在test_case下面編寫用例,如下簡單的示範了在百度上搜索關鍵字和點選設定的操作:

檔名為:test_baidu.py

# -*- coding: utf-8 -*-
from selenium import webdriver
import unittest, time, re

class MyTest(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "https://www.baidu.com"
        self.accept_next_alert = True

    def test_02baidu_search(self):
        u''' 測試百度搜索'''
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys("selenium-test")
        driver.find_element_by_id("su").click()
        print("test_baidu__test_02baidu_search")

    def test_01baidu_setting(self):
        u''' 測試百度首頁設定 '''
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_css_selector("div#u1 a.pf").click()
        driver.find_element_by_class_name("setpref").click()
        driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
        driver.switch_to_alert().accept()
        print("test_baidu__test_01baidu_setting")

    def tearDown(self):
        self.driver.close()

#從all_test中呼叫時,可以不要這個
if __name__ == "__main__":
    unittest.main()

為了顯示出組織測試用例的效果,我們將此檔案再複製一份,把檔名和方法名等修改一下:

檔名為:test_baidu2.py

# -*- coding: utf-8 -*-
from selenium import webdriver
import unittest, time, re

class MyTest(unittest.TestCase):
    u''' 測試baidu的第二個用例'''
    def setUp(self):
        self.driver = webdriver.Firefox()
        self.driver.implicitly_wait(30)
        self.base_url = "https://www.baidu.com"
        self.accept_next_alert = True

    def test_02baidu_search(self):
        u''' 測試baidu的第二個用例的test_02baidu_search'''
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_id("kw").click()
        driver.find_element_by_id("kw").clear()
        driver.find_element_by_id("kw").send_keys("selenium-test")
        driver.find_element_by_id("su").click()
        print("test_baidu2__test_02baidu_search")


    def test_01baidu_setting(self):
        u''' 測試baidu的第二個用例的test_01baidu_setting'''
        driver = self.driver
        driver.get(self.base_url + "/")
        driver.find_element_by_css_selector("div#u1 a.pf").click()
        driver.find_element_by_class_name("setpref").click()                        driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
        driver.switch_to_alert().accept()
        print("test_baidu2__test_01baidu_setting")

    def tearDown(self):
        self.driver.close()

if __name__ == "__main__":
    unittest.main()

~

2. 美化報告樣式和傳送結果郵件
上面我們寫了 兩個測試用例作為示例,我們也可以新增更多的進去。接著我們使用HTMLTestRunner這個開源模組來美化測試報告,關於它的下載使用可以參考https://pypi.python.org/pypi/HTMLTestRunner。然後,我們可以在程式碼中寫上執行完成之後自動傳送測試郵件出來,便於我們檢視。請參看以下程式碼:

#coding=utf-8
import unittest
import smtplib
from email.mime.text import MIMEText
from email.header import Header
import time
import HTMLTestRunner
from email.mime.application import MIMEApplication

#---傳送郵件---
def send_email(report_file):
    sender = "[email protected]"
    receiver = "[email protected]"
    smtpserver = "smtp.qq.com"
    #傳送郵箱的賬號密碼,此處使用的是qq郵箱和第三方登入的授權碼
    username = "[email protected]"
    password = "gfomcomojtuudijc"

    #定義郵件正文
    file = open(report_file,"rb")
    mail_body = file.read()
    file.close()

    msg = MIMEText(mail_body, _subtype="html", _charset="utf-8")
    msg["Subject"] = u"自動化測試報告"

    smtp = smtplib.SMTP_SSL("smtp.qq.com")
    smtp.login(username, password)
    smtp.sendmail(sender, receiver, msg.as_string())
    smtp.quit()
    print("Email has send out !")

#---將用例新增到測試套件---
def creatsuite():
    testunit=unittest.TestSuite()
    test_dir = "D:\\Test_Project\\test_case"
    discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py",
                                                 top_level_dir = None)
    for test_suite in discover:
        for test_case in test_suite:
            testunit.addTest(test_case)
            print (testunit)
    return testunit

if __name__ == "__main__":
    current_time = time.strftime("%Y-%m-%d-%H-%M")
    report_dir = "D:\\Test_Project\\test_report\\"
    report_file = report_dir + current_time + "-Test_Result.html"
    report_stream = open(report_file, "wb")
    # runner = unittest.TextTestRunner()
    # 注意HTMLTestRunner只支援python2
    runner = HTMLTestRunner.HTMLTestRunner(stream=report_stream,title=u"自動化測試報告",  description=u"用例執行情況如下:")
    runner.run(creatsuite())
    report_stream.close()
    send_email(report_file)

在上面的程式碼中我們使用了runner = HTMLTestRunner.HTMLTestRunner()方法來代替runner = unittest.TextTestRunner(),是為了使用HTMLTestRunner這個模組來美化和輸出美觀的報告。然後呼叫方法來發送郵件。執行此檔案後,可以得到以下輸出的報告:

HTMLTestRunner

可以看見使用這個可以清晰的看到用例的執行情況,也便於檢視失敗用例的原因去除錯它。
同時,在我們輸入的收件箱裡也會受到一份通知郵件,我們可以將此輸出報告新增到郵件的正文或附件中,以便於檢視。