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


什么是unittest

這里我們將要用的unittest是python的單元測試框架,它的官網是 https://docs.python.org/2/library/unittest.html,在這里我們可以得到全面的信息。

當我們寫的用例越來越多時,我們就需要考慮用例編寫的規范與組織,以便於后期的維護,而unittest正是這樣一款工具。我們這里用一個示例來展示用unittest腳本是什么樣子的

 1 # -*- coding: utf-8 -*-
 2 from selenium import webdriver
 3 from selenium.webdriver.common.by import By
 4 from selenium.webdriver.common.keys import Keys
 5 from selenium.webdriver.support.ui import Select
 6 from selenium.common.exceptions import NoSuchElementException
 7 from selenium.common.exceptions import NoAlertPresentException
 8 #導入unittest包
 9 import unittest, time, re
10 
11 #SearchTest類繼承自unittest.TestCase,表明這是一個測試案例
12 class SearchTest(unittest.TestCase):
13     #setUp用於初始化工作
14     def setUp(self):
15         self.driver = webdriver.Firefox()
16         self.driver.implicitly_wait(30)
17         self.base_url = "https://www.baidu.com/"
18         self.verificationErrors = []
19         self.accept_next_alert = True
20 
21     #以test開頭的是我們的測試腳本    
22     def test_search(self):
23         driver = self.driver
24         driver.get(self.base_url + "/")
25         driver.find_element_by_id("kw").click()
26         driver.find_element_by_id("kw").clear()
27         driver.find_element_by_id("kw").send_keys("selenium2")
28         driver.find_element_by_id("su").click()
29 
30     def is_element_present(self, how, what):
31         try: self.driver.find_element(by=how, value=what)
32         except NoSuchElementException as e: return False
33         return True
34 
35     def is_alert_present(self):
36         try: self.driver.switch_to_alert()
37         except NoAlertPresentException as e: return False
38         return True
39 
40     def close_alert_and_get_its_text(self):
41         try:
42             alert = self.driver.switch_to_alert()
43             alert_text = alert.text
44             if self.accept_next_alert:
45                 alert.accept()
46             else:
47                 alert.dismiss()
48             return alert_text
49         finally: self.accept_next_alert = True
50 
51     #在每個測試方法后執行,完成清理工作
52     def tearDown(self):
53         self.driver.quit()
54         self.assertEqual([], self.verificationErrors)
55 
56 #整個測試過程集中在unittest的main()模塊中,其默認執行以test開頭的方法
57 if __name__ == "__main__":
58     unittest.main()
View Code

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

unittest中的概念

TestCase

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

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

TestSuite

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

TextTestRunner

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

TestFixture

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

知道了這幾個主要的概念,我們就可以把上面的腳本中的最后一行unittest.main(),改為以下代碼:

1  #構造測試套件
2     suite = unittest.TestSuite()
3     suite.addTest(SearchTest("test_search"))
4     #執行測試
5     runner = unittest.TextTestRunner()
6     runner.run(suite)

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

用例組織

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

執行測試用例方案一

直接用

unittest.main()

執行,這里它搜索所有以test開頭的測試用例方法,按照ASCII的順序執行多個用例。

執行測試用例方案二

先實例化測試套件,將用例加載進去,再用TextTestRunner去執行用例:

1  suite=unittest.TestSuite()
2  suite.addTest(Test('test_case2'))
3  suite.addTest(Test('test_case1'))
4  runner=unittest.TextTestRunner()
5  runner.run(suite)

執行的順序是用例的加載順序,比如這里是先執行2后執行1。

執行測試用例方案三

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

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

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

如果這里指定的目錄下面有多個經pattern匹配上的.py文件呢?調用discover方法,首先通過test_dir定義查找目錄,如果文件名滿足定義的pattern,那么我們要用for循環來找出所有被篩選出來的用例,並將其循環加到套件中,主要代碼如下:

1  for test_suite in discover:
2         for test_case in test_suite:
3             test_unit.addTests(test_case)

由上面組織用例的方式我們可以知道,在實際的測試用腳本開發中,我們可以在目錄下創建xx.py的文件,當用例穩定運行后,可以修改成test_xx.py,以便於添加到測試套件中。注意,文件名的匹配規則,我們可以隨便由pattern參數定義。

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

一個例子

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

編寫測試用例

在test_case下面編寫用例,如下簡單的示范了在百度上搜索關鍵字和點擊設置的操作:

 1 文件名為:test_baidu.py
 2 
 3 # -*- coding: utf-8 -*-
 4 from selenium import webdriver
 5 import unittest, time, re
 6 
 7 class MyTest(unittest.TestCase):
 8 
 9     def setUp(self):
10         self.driver = webdriver.Firefox()
11         self.driver.implicitly_wait(30)
12         self.base_url = "https://www.baidu.com"
13         self.accept_next_alert = True
14 
15     def test_02baidu_search(self):
16         u''' 測試百度搜索'''
17         driver = self.driver
18         driver.get(self.base_url + "/")
19         driver.find_element_by_id("kw").click()
20         driver.find_element_by_id("kw").clear()
21         driver.find_element_by_id("kw").send_keys("selenium-test")
22         driver.find_element_by_id("su").click()
23         print("test_baidu__test_02baidu_search")
24 
25     def test_01baidu_setting(self):
26         u''' 測試百度首頁設置 '''
27         driver = self.driver
28         driver.get(self.base_url + "/")
29         driver.find_element_by_css_selector("div#u1 a.pf").click()
30         driver.find_element_by_class_name("setpref").click()
31         driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
32         driver.switch_to_alert().accept()
33         print("test_baidu__test_01baidu_setting")
34 
35     def tearDown(self):
36         self.driver.close()
37 
38 #從all_test中調用時,可以不要這個
39 if __name__ == "__main__":
40     unittest.main()
View Code

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

 1 文件名為:test_baidu2.py
 2 
 3 # -*- coding: utf-8 -*-
 4 from selenium import webdriver
 5 import unittest, time, re
 6 
 7 class MyTest(unittest.TestCase):
 8     u''' 測試baidu的第二個用例'''
 9     def setUp(self):
10         self.driver = webdriver.Firefox()
11         self.driver.implicitly_wait(30)
12         self.base_url = "https://www.baidu.com"
13         self.accept_next_alert = True
14 
15     def test_02baidu_search(self):
16         u''' 測試baidu的第二個用例的test_02baidu_search'''
17         driver = self.driver
18         driver.get(self.base_url + "/")
19         driver.find_element_by_id("kw").click()
20         driver.find_element_by_id("kw").clear()
21         driver.find_element_by_id("kw").send_keys("selenium-test")
22         driver.find_element_by_id("su").click()
23         print("test_baidu2__test_02baidu_search")
24 
25 
26     def test_01baidu_setting(self):
27         u''' 測試baidu的第二個用例的test_01baidu_setting'''
28         driver = self.driver
29         driver.get(self.base_url + "/")
30         driver.find_element_by_css_selector("div#u1 a.pf").click()
31         driver.find_element_by_class_name("setpref").click()                        driver.find_element_by_css_selector("div#gxszButton>a.prefpanelgo").click()
32         driver.switch_to_alert().accept()
33         print("test_baidu2__test_01baidu_setting")
34 
35     def tearDown(self):
36         self.driver.close()
37 
38 if __name__ == "__main__":
39     unittest.main()
View Code

美化報告樣式和發送結果郵件

上面我們寫了 兩個測試用例作為示例,我們也可以添加更多的進去。接着我們使用HTMLTestRunner這個開源模塊來美化測試報告,關於它的下載使用可以參考https://pypi.python.org/pypi/HTMLTestRunner。然后,我們可以在代碼中寫上運行完成之后自動發送測試郵件出來,便於我們查看。請參看以下代碼:

 1 #coding=utf-8
 2 import unittest
 3 import smtplib
 4 from email.mime.text import MIMEText
 5 from email.header import Header
 6 import time
 7 import HTMLTestRunner
 8 from email.mime.application import MIMEApplication
 9 
10 #---發送郵件---
11 def send_email(report_file):
12     sender = "XXXXXX@qq.com"
13     receiver = "XXXXXX@qq.com"
14     smtpserver = "smtp.qq.com"
15     #發送郵箱的賬號密碼,此處使用的是qq郵箱和第三方登錄的授權碼
16     username = "XXXXXX@qq.com"
17     password = "gfomcomojtuudijc"
18 
19     #定義郵件正文
20     file = open(report_file,"rb")
21     mail_body = file.read()
22     file.close()
23 
24     msg = MIMEText(mail_body, _subtype="html", _charset="utf-8")
25     msg["Subject"] = u"自動化測試報告"
26 
27     smtp = smtplib.SMTP_SSL("smtp.qq.com")
28     smtp.login(username, password)
29     smtp.sendmail(sender, receiver, msg.as_string())
30     smtp.quit()
31     print("Email has send out !")
32 
33 #---將用例添加到測試套件---
34 def creatsuite():
35     testunit=unittest.TestSuite()
36     test_dir = "D:\\Test_Project\\test_case"
37     discover = unittest.defaultTestLoader.discover(test_dir, pattern="test*.py",
38                                                  top_level_dir = None)
39     for test_suite in discover:
40         for test_case in test_suite:
41             testunit.addTest(test_case)
42             print (testunit)
43     return testunit
44 
45 if __name__ == "__main__":
46     current_time = time.strftime("%Y-%m-%d-%H-%M")
47     report_dir = "D:\\Test_Project\\test_report\\"
48     report_file = report_dir + current_time + "-Test_Result.html"
49     report_stream = open(report_file, "wb")
50     # runner = unittest.TextTestRunner()
51     # 注意HTMLTestRunner只支持python2
52     runner = HTMLTestRunner.HTMLTestRunner(stream=report_stream,title=u"自動化測試報告",  description=u"用例執行情況如下:")
53     runner.run(creatsuite())
54     report_stream.close()
55     send_email(report_file)
View Code

在上面的代碼中我們使用了runner = HTMLTestRunner.HTMLTestRunner()方法來代替runner = unittest.TextTestRunner(),是為了使用HTMLTestRunner這個模塊來美化和輸出美觀的報告。然后調用方法來發送郵件。運行此文件后,可以得到以下輸出的報告:

可以看見使用這個可以清晰的看到用例的執行情況, 也便於查看失敗用例的原因去調試它,同時在們輸入的收件箱也會收到一份通知郵件,我們可以將此輸出報告添加到正文或附件中,以便於查看


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM