目錄
3.9 多層框架/視窗定位:frame.html、inner.html
3.12 alert、confirm、prompt的處理:alert.html、send.html
自動化介紹:自動化測試指軟體測試的自動化,在預設狀態下執行應用程式或者系統,預設條件包括正常和異常,最後評估執行結果。將人爲驅動的測試行爲轉化爲機器執行的過程。
1、常見自動化測試工具:
2、selenium 的諸多優點:
3、自動化測試方法:
測試物件:UI、介面、程式碼
測試過程:系統測試、整合測試、單元測試
執行人員:測試人員、開發人員
測試時機:功能相對穩定的情況下
- 自動化測試可以在整個測試過程中任何一個階段實施,前提功能相對穩定
- 測試人員一般在系統測試時進行自動化測試
- 整合測試階段多進行自動構建、部署,以及冒煙測試的自動化
- 單元測試針對程式碼級別進行測試,可進行靜態程式碼檢查,或者執行單元測試用例,典型的框架比如junit,jmock等,該部分多由開發人員實施
UI 自動化:
測試前提:前端頁面完成
- 用例維護量大
- 頁面相關性強,必須後期介入
- UI測試適合與介面變動較小的專案
介面自動化:
介面自動化前提:後端開發完成
- 可在產品前期介入
- 用例維護量小
- 頁面相關性小
- 適合介面變動較小,介面變動頻繁的專案
降低大型系統的由於變更或者多期開發引起的大量的迴歸測試的人力投入,這可能是自動化測試最主要的任務,特別是在程式修改比較頻繁時,效果是非常明顯的,自動化測試前期人力投入較多,但後期進入維護期後,可節省大量人力,而手工測試後期需要增加大量人力用於迴歸測試。自動化指令碼的利用率越高,價值越大。迴歸測試、相容性測試做成自動化更好。
1、自動化測試適用物件
實施自動化測試的前提條件:需求變動不頻繁、專案週期足夠長、自動化測試指令碼可重複使用
適合做自動化的專案:
2、什麼時候實施
建議:可預見的需求不影響自動化測試用例的設計
3、如何實施自動化測試
單純的講,自動化測試的具體實現,應該是包含下面 下麪七個過程的。
4、自動化測試實施失敗的因素
Selenium是ThroughtWorks公司一個強大的開源Web功能測試工具系列,支援多平臺、多瀏覽器、多語言去實現自動化測試,Selenium2將瀏覽器原生的API封裝成WebDriver API,可以直接操作瀏覽器頁面裡的元素,甚至操作瀏覽器本身(截圖,視窗大小,啓動,關閉,安裝外掛,設定證書之類的),所以就像真正的使用者在操作一樣。
支援多種開發語言:ruby,python,java,perl,c#等,同時Selenium 測試直接自動執行在瀏覽器中,就像真正的使用者在手工操作一樣。
支援的瀏覽器包括 IE、Chrome 和 Firefox等。
1、selenium1 :selenium IDE(錄製指令碼),selenium RC(控制瀏覽器的),selenium GRID(分佈式)
主要問題:用JavaScript寫的有環境沙箱問題,會阻止一些彈窗alert
Selenium IDE
Selenium IDE (整合開發環境) 是一個建立測試指令碼的原型工具。它是一個 Firefox 外掛,實現簡單的瀏覽器操作的錄製與回放功能,提供建立自動化測試的建議介面。Selenium IDE 有一個記錄功能,能記錄使用者的操作,並且能選擇多種語言把它們導出到一個可重用的指令碼中用於後續執行。
Selenium RC
Selenium RC 是selenium 家族的核心工具,Selenium RC 支援多種不同的語言編寫自動化測試指令碼,通過selenium RC 的伺服器作爲代理伺服器去存取應用從而達到測試的目的。
selenium RC 使用分Client Libraries 和Selenium Server。
Selenium Grid
Selenium Grid 使得 Selenium RC 解決方案能提升針對大型的測試套件或者哪些需要執行在多環境的測試套件的處理能力。Selenium Grid 能讓你並行的執行你的測試,也就是說,不同的測試可以同時跑在不同的遠端機器上。這樣做有兩個有事,首先,如果你有一個大型的測試套件,或者一個跑的很慢的測試套件,你可以使用 Selenium Grid 將你的測試套件劃分成幾份同時在幾個不同的機器上執行,這樣能顯著的提升它的效能。同時,如果你必須在多環境中執行你的測試套件,你可以獲得多個遠端機器的支援,它們將同時執行你的測試套件。在每種情況下,Selenium Grid 都能通過並行處理顯著地縮短你的測試套件的處理時間。
2、selenium2 :Webdriver(解決環境沙箱問題)
Webdriver原理:驅動瀏覽器進行操作
3、selenium3 :增加了一些瀏覽器的原生驅動,edge,Safari,geckodriver
一個簡單指令碼:
# coding = utf-8 #可加可不加,開發人員喜歡加一下,防止亂碼。
from selenium import webdriver #要想使用selenium 的webdriver 裡的函數,首先把包導進來
import time
browser = webdriver.Firefox() #我們需要操控的瀏覽器
time.sleep(3) #等待三秒
browser.get("http://www.baidu.com")
time.sleep(3)
browser.find_element_by_id("kw").send_keys("selenium") #一個控制元件有若幹屬性id 、name、(也可以用其它方式定位),百度輸入框的id 叫kw ,我要在輸入框裡輸入selenium 。
time.sleep(3)
browser.find_element_by_id("su").click() #搜尋的按鈕的id 叫su ,我需要點一下按鈕( click() )。
browser.quit() #退出並關閉視窗的每一個相關的驅動程式
#brower.close() #關閉當前視窗。
'''
close方法關閉當前的瀏覽器視窗,quit方法不僅關閉視窗,還會徹底的退出webdriver,釋放與driver server之間
的連線。所以簡單來說quit是更加徹底的close,quit會更好的釋放資源
'''
要操作一個物件,首先應該識別這個物件,就像一個人一樣,我們可以通過身份證號,住址等找到他,物件也有類似的屬性,但是不管用哪種方式,必須保證頁面上該屬性的唯一性。
webdriver 提供了一系列的物件定位方法,常用的有以下幾種:
檢視物件元素方法:在瀏覽器中找到需要檢視的元素,右鍵點選最下面 下麪的檢查就可以啦!
如何知道 CSS 與 XPath?右鍵點選 Copy,就會出現一些內容,選擇自己想要的。
元素定位解釋:
方法Method | 參數Argument | 範例Example |
id | id: 需要被查詢的元素的ID | dr.find_element_by_id('search') |
name | name: 需要被查詢的元素的名稱 | dr.find_element_by_name('q') |
class name | class_name: 需要被查詢的元素的類名 | dr.find_element_by_class_name('input-text') |
tag_name | tag: 需要被查詢的元素的標籤名稱 | dr.find_element_by_tag_name('input') |
link_text | link_text: 需要被查詢的元素的鏈接文字 | dr.find_element_by_link_text('Log In') |
partial_link_text | link_text: 需要被查詢的元素的部分鏈接文字 | dr.find_element_by_partial_link_text('Long') |
xpath | xpath: 需要被查詢的元素的xpath | dr.find_element_by_xpath('//*[@id="xx"]/a') |
css_selector | css_selector: 需要被查詢的元素的ID | dr.find_element_by_css_selector('#search') |
上面3.1講述了元素定位,那麼定位到元素之後需要對元素進行操作,是進行滑鼠點選還是鍵盤輸入,取決於我們定位的是按鈕還是輸入框。一般來說,webdriver 中比較常用的操作物件的方法有下面 下麪幾個:
方法 | 描述 | 範例 |
click | 用於點選一個按鈕 | brower.find_element_by_id('su').click() |
submit | 提交表單,將"百度一下"的click換成submit效果一樣 | brower.find_element_by_id('su').submit() |
clear | 清除輸入框內容 | brower.find_element_by_id('kw').clear() |
send_keys | 在一個輸入框輸入內容 | brower.find_element_by_id('kw').send_keys("CSDN") |
text | 獲取元素的文字資訊 | browser.find_element_by_id("s-bottom-layer-right").text |
3.1和3.2程式碼範例:
# coding = utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome() # 獲得Google的驅動
time.sleep(2)
browser.get("http://www.baidu.com")
#通過 id 定位到 input 輸入框,然後輸入待搜尋內容:CSDN
'''browser.find_element_by_id("kw").send_keys("CSDN")'''
#搜尋的按鈕的 id 叫做 su ,我需要點一下按鈕 click()
'''browser.find_element_by_id("su").click()'''
# 通過name定位
'''
browser.find_element_by_name("wd").send_keys("網易雲音樂")
browser.find_element_by_id("su").click()'''
# 通過鏈接內容定位
'''
browser.find_element_by_link_text("高考加油").click()
browser.find_element_by_partial_link_text("高考").click()'''
#通過tag name 方式定位, 不能成功,因爲input太多了不唯一。
'''browser.find_element_by_tag_name("input").send_keys("selenium")'''
# CSS 定位,id --> #id名,class唯一時 --> .class名
'''
browser.find_element_by_css_selector("#kw").send_keys("hello")
browser.find_element_by_css_selector(".s_ipt").send_keys("hello")
'''
# XPath定位,* 表示將前面的內容全部省略掉,用 id 定位
'''
#browser.find_element_by_xpath("//*[@id='kw']").send_keys("521")
browser.find_element_by_xpath("//*[@name='wd']").send_keys("521")
browser.find_element_by_xpath("//*[@id='su']").click()
'''
# 先在文字方塊輸入 「CSDN",再清除內容,再次輸入」元龍「進行搜尋
'''
browser.find_element_by_id("kw").send_keys("CSDN")
time.sleep(2)#等待兩秒鐘,不等待的話會出錯,頁面載入需要時間
browser.find_element_by_id("kw").clear()
time.sleep(2)
browser.find_element_by_id("kw").send_keys("元龍")
# browser.find_element_by_id("su").click()
browser.find_element_by_id("su").submit()
'''
# 獲取元素文字
# print(browser.find_element_by_id("s-bottom-layer-right").text)
新增休眠非常簡單,我們需要引入time 包,就可以在指令碼中自由的新增休眠時間了
import time
time.sleep(3)
智慧等待:implicitly_wait()
通過新增implicitly_wait() 方法就可以方便的實現智慧等待;implicitly_wait(30)的用法應該比time.sleep() 更智慧,後者只能選擇一個固定的時間的等待,前者可以在一個時間範圍內智慧的等待。
# coding = utf-8
from selenium import webdriver
import time #調入time 函數
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
browser.implicitly_wait(30) #智慧等待30秒
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
browser.quit()
列印 title 和 URL,雖然沒看到指令碼執行過程,但在執行結果可以看到列印的內容,說明頁面正確被打開了:
print(driver.title) #百度一下,你就知道
print(driver.current_url) #http://www.baidu.com
1、瀏覽器最大化:呼叫啓動的瀏覽器不是全螢幕的,這樣不會影響指令碼的執行,但是有時候會影響我們「觀看」指令碼的執行。
瀏覽器還可以最小化和設定固定大小。browser.set_window_size( 寬,高)
driver.maximize_window()# 瀏覽器最大化
#driver.minimize_window()# 瀏覽器最小化
# time.sleep(2)
# driver.set_window_size(400,600)# 瀏覽器設定固定大小
2、瀏覽器的前進與後退:瀏覽器上有一個後退、前進按鈕,對於瀏覽網頁的人是比較方便的
driver.back()#後退
driver.forward()#前進
3、控制瀏覽器卷軸:execute_script(script, *args),在當前視窗/框架同步執行javaScript
js = "var q=document.documentElement.scrollTop=10000" #卷軸拖到最底端
driver.execute_script(js)
time.sleep(2)
js = "var q=document.documentElement.scrollTop=0" #卷軸拖到最頂端
driver.execute_script(js)
#windows.scollTo(x-coord,y-coord),x-coord 是文件中的橫軸座標,y-coord 是文件中的縱軸座標。控制瀏覽器的上下左右
driver.execute_script("window.scrollTo(1000,0)") #卷軸拖到最右端
time.sleep(2)
driver.execute_script("window.scrollTo(0,1000)") #卷軸拖到最左端
1、鍵盤按鍵用法:
要想呼叫鍵盤按鍵操作需要引入keys 包:
from selenium.webdriver.common.keys import Keys
通過send_keys()呼叫按鍵:
send_keys(Keys.TAB) # TAB
send_keys(Keys.ENTER) # 回車
程式碼:
driver.get("http://127.0.0.1:81/biz/user-login.html")
driver.implicitly_wait(10)
driver.maximize_window() # 瀏覽器全螢幕顯示
# TAB鍵和 Inter 鍵的用法
#tab 的定位相當於清除了密碼框的預設提示資訊,等同上面的clear()
driver.find_element_by_id("account").send_keys("admin")
driver.find_element_by_id("account").send_keys(Keys.TAB)
time.sleep(2)
#也可定位登陸按鈕,通過enter(回車)代替click()
driver.find_element_by_name("password").send_keys("123456")
driver.find_element_by_id("login").send_keys(Keys.ENTER)
2、鍵盤組合鍵用法:全選和剪下
# 組合鍵用法:先輸入天官賜福進行搜尋,然後把輸入框內容全選剪下後再次輸入霧山五行進行搜尋
driver.implicitly_wait(10)
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a') #全選
time.sleep(2)
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x') #剪下
time.sleep(2)
driver.find_element_by_id("kw").send_keys("霧山五行")
driver.find_element_by_id("su").click()
Web測試中,有關滑鼠的操作,不只是單擊,有時候還要做右擊、雙擊、拖動等操作。這些操作包含在ActionChains類中。
常用的滑鼠方法:
需要引入 ActionChains 類:
from selenium.webdriver.common.action_chains import ActionChains
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Chrome()
driver.get("http://news.baidu.com")
qqq =driver.find_element_by_xpath(".//*[@id='s_btn_wr']")
ActionChains(driver).context_click(qqq).perform() #右鍵
ActionChains(driver).double_click(qqq).perform() #雙擊
#定位元素的原位置
element = driver.find_element_by_id("s_btn_wr")
#定位元素要移動到的目標位置
target = driver.find_element_by_class_name("btn")
#執行元素的移動操作
ActionChains(driver).drag_and_drop(element, target).perform()
下面 下麪開始會用到一些 html 檔案進行 API 的輔助理解,檔案我放在 github 上,大家可以開啓便於理解,在介紹部分功能時我會放部分程式碼,完整的在這個模組最下面 下麪給出。
html檔案:https://github.com/Consini/other/tree/master/selenium/html
webdriver 可以很方便的使用findElement 方法來定位某個特定的物件,不過有時候我們卻需要定位一組物件,這時候就需要使用findElements 方法。
定位一組物件一般用於以下場景:
get_attribute:獲得屬性值。
# 選擇頁面上所有的input,然後從中過濾出所有的 checkbox 並勾選之
inputs = driver.find_elements_by_tag_name('input')
for input in inputs:
if input.get_attribute('type') == 'checkbox':
input.click()
多層框架或視窗的定位:
有時候我們定位一個元素,定位器沒有問題,但一直定位不了,這時候就要檢查這個元素是否在一個frame 中,seelnium webdriver 提供了一個switch_to_frame 方法,可以很輕鬆的來解決這個問題。
switch_to_frame(name):用switch_to_frame方法去獲取frame中嵌入的頁面,對那個頁面裡的元素進行定位。
switch_to_default_content:從frame中嵌入的頁面裡跳出,跳回到最外面的原始頁面中。
driver.switch_to_window("windowName"):有可能巢狀的不是框架,而是視窗,還有針對視窗的方法
#從預設頁面跳轉到 ifrome1(id = f1)
driver.switch_to.frame("f1")
#再找到其下面 下麪的 ifrome2 (id = f2)
driver.switch_to.frame("f2")
#下面 下麪就可以正常的操作元素了
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(2)
# 跳轉到預設頁面
driver.switch_to.default_content()
driver.switch_to.frame("f1")
driver.find_element_by_link_text("click").click()
定位思路:具體思路是:先點選顯示出1個下拉式選單,然後再定位到該下拉式選單所在的ul,再定位這個ul 下的某個具體的link。在這裏,我們定位第1個下拉式選單中的Action 這個選項。
#點選Link1鏈接(彈出下拉選單)
driver.find_element_by_link_text('Link1').click()
driver.implicitly_wait(10)
#方法 1:通過文字鏈接Action定位
action = driver.find_element_by_link_text('Action')
driver.implicitly_wait(10)
webdriver.ActionChains(driver).move_to_element(action).perform()
#方法 2: 找到id 爲dropdown1的父元素
# WebDriverWait(driver,10).until(lambda the_driver:
# the_driver.find_element_by_id('dropdown1').is_displayed())
# #在父親元件下找到link 爲Action 的子元素
# menu = driver.find_element_by_id('dropdown1').find_element_by_link_text('Action')
# #滑鼠定位到子元素上
# webdriver.ActionChains(driver).move_to_element(menu).perform()
下拉框是我們最常見的一種頁面元素,對於一般的元素,我們只需要一次就定位,但下拉框裡的內容需要進行兩次定位,先定位到下拉框,再定位到下拉框內裡的選項。
現在我們來通過指令碼選擇下拉選單裏的$10.69
# 方法1:進行兩次定位
#先定位到下拉框
m=driver.find_element_by_id("ShippingMethod")
#再點選下拉框下的選項
m.find_element_by_xpath("//option[@value='10.69']").click()
# 方法1.2:xpath 定位
driver.find_element_by_xpath("//*[@id='ShippingMethod']/option[3]").click()
# 方法2: 先定位一組元素
lists = driver.find_elements_by_tag_name("option")
for list in lists:#遍歷尋找自己想要的
if list.get_attribute('value') == '10.69':
list.click()
lists[2].click() # 通過下標定位
alert.html是一個頁面點選按鈕後出現警告框,然後列印警告框內容,send.html是警告框中會有一個輸入框,在裏面輸入內容。
注意:switch_to_alert()只能處理原生的alert
alert = driver.switch_to.alert#接受警告資訊
time.sleep(1)
print("alert text:"+alert.text)#列印警告資訊
alert.accept()#確定
實現的是點選 click 彈出警告框,警告框中還有一個 div,點選 click me 後上面的文字資訊會改變。
上傳過程一般要開啓一個本地視窗,從視窗選擇本地檔案新增。所以,一般會卡在如何操作本地視窗新增上傳檔案。只要定位上傳按鈕,通過send_keys 新增本地檔案路徑就可以了。絕對路徑和相對路徑都可以,關鍵是上傳的檔案存在。
driver.get(file_path)
driver.find_element_by_name("file").send_keys("D:\\Program Files\\python\\project\\selenium\\html\\upload.html")
關於 html 檔案的所有操作:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import time
import os
driver = webdriver.Chrome()
driver.maximize_window()
# 定位一組元素,get_attribute:獲得屬性值
'''
file_path = 'file:///' + os.path.abspath('html\\checkbox.html')
driver.get(file_path)
driver.maximize_window()
# 選擇頁面上所有的input,然後從中過濾出所有的 checkbox 並勾選之
inputs = driver.find_elements_by_tag_name('input')
for input in inputs:
if input.get_attribute('type') == 'checkbox':
input.click()
'''
# 多層視窗定位:通過switch_to_frame() 方法來進行定位
'''
file_path = 'file:///' + os.path.abspath('html\\frame.html')
driver.get(file_path)
driver.implicitly_wait(30)
#從預設頁面跳轉到 ifrome1(id = f1)
driver.switch_to.frame("f1")
#再找到其下面 下麪的 ifrome2 (id = f2)
driver.switch_to.frame("f2")
#下面 下麪就可以正常的操作元素了
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(2)
# 跳轉到預設頁面
driver.switch_to.default_content()
driver.switch_to.frame("f1")
driver.find_element_by_link_text("click").click()
'''
# 層級定位
'''
file_path = 'file:///' + os.path.abspath('html\\level_locate.html')
driver.get(file_path)
#點選Link1鏈接(彈出下拉選單)
driver.find_element_by_link_text('Link1').click()
driver.implicitly_wait(10)
#方法 1:通過文字鏈接Action定位
action = driver.find_element_by_link_text('Action')
driver.implicitly_wait(10)
webdriver.ActionChains(driver).move_to_element(action).perform()
#方法 2: 找到id 爲dropdown1的父元素
# WebDriverWait(driver,10).until(lambda the_driver:
# the_driver.find_element_by_id('dropdown1').is_displayed())
# #在父親元件下找到link 爲Action 的子元素
# menu = driver.find_element_by_id('dropdown1').find_element_by_link_text('Action')
# #滑鼠定位到子元素上
# webdriver.ActionChains(driver).move_to_element(menu).perform()
'''
#下拉框處理
'''
file_path = 'file:///' + os.path.abspath('html\\drop_down.html')
driver.get(file_path)
# 方法1:xpath 定位
driver.find_element_by_xpath("//*[@id='ShippingMethod']/option[3]").click()
# 方法2: 先定位一組元素
lists = driver.find_elements_by_tag_name("option")
for list in lists:#遍歷尋找自己想要的
if list.get_attribute('value') == '10.69':
list.click()
lists[2].click() # 通過下標定位
'''
# alert 框處理:switch_to.alert
# text 返回alert/confirm/prompt 中的文字資訊
# accept 點選確認按鈕
# dismiss 點選取消按鈕,如果有的話
'''
file_path = 'file:///' + os.path.abspath('html\\alert.html')
driver.get(file_path)
driver.find_element_by_id("tooltip").click()# 在這個例子中 id,link text 都可以用來定位
time.sleep(1)
alert = driver.switch_to.alert
time.sleep(1)
print("alert text:"+alert.text)
alert.accept()
'''
# send_keys 輸入值,這個alert\confirm 沒有對話方塊就不能用了,不然會報錯
'''
file_path = 'file:///' + os.path.abspath('html\\send.html')
driver.get(file_path)
#driver.find_element_by_xpath("/html/body/input").click()
driver.find_element_by_tag_name("input").click()
alert = driver.switch_to.alert
alert.send_keys("少卿大人")
alert.accept()
'''
# div 模組的處理
'''
file_path = 'file:///' + os.path.abspath('html\\modal.html')
driver.get(file_path)
time.sleep(3)
driver.find_element_by_id("show_modal").click()
time.sleep(1)
ddiv = driver.find_element_by_class_name("modal-body")
ddiv.find_element_by_id("click").click()
#ddiv.find_element_by_partial_link_text("click").click()
time.sleep(1)
#button = driver.find_element_by_class_name("modal-footer").find_elements_by_tag_name("button")
buttons = driver.find_elements_by_tag_name("button")
buttons[0].click()
'''
# 上傳檔案操作
file_path = 'file:///' + os.path.abspath('html\\upload.html')
driver.get(file_path)
driver.find_element_by_name("file").send_keys("D:\\Program Files\\python\\project\\selenium\\html\\upload.html")
time.sleep(2)
driver.quit()
unittest 單元測試提供了建立測試用例,測試套件以及批次執行的方案, unittest 在安裝pyhton 以後就直接自帶了,直接import unittest 就可以使用。
作爲單元測試的框架, unittest 也是可以對程式最小模組的一種敏捷化的測試。在自動化測試中,我們雖然不需要做白盒測試,但是必須需要知道所使用語言的單元測試框架。利用單元測試框架,建立一個類,該類繼承unittest的TestCase,這樣可以把每個case看成是一個最小的單元, 由測試容器組織起來,到時候直接執行,同時引入測試報告。
setUp():進行初始化,定義全域性變數:定義方法 : 類的範例.變數:self.driver
tearDown():進行清理工作,
可以增加verbosity參數,例如unittest.main(verbosity=2)
在主函數中,直接呼叫main() ,在main中加入verbosity=2 ,這樣測試的結果就會顯示的更加詳細。
這裏的verbosity 是一個選項, 表示測試結果的資訊複雜度,有三個值:
0 ( 靜默模式): 你只能獲得總的測試用例數和總的結果比如總共100個失敗,20 成功80
1 ( 預設模式): 非常類似靜默模式只是在每個成功的用例前面有個「 . 」 每個失敗的用例前面有個「F」
2 ( 詳細模式): 測試結果會顯示每個測試用例的所有相關的資訊
1、構建測試套件
完整的單元測試很少只執行一個測試用例,開發人員通常都需要編寫多個測試用例才能 纔能對某一軟體功能進行比較完整的測試,這些相關的測試用例稱爲一個測試用例集,在unittest中是用TestSuite 類來表示的。假設我們已經編寫了testbaidu1.pytestbaidu2.py兩個檔案,那麼我們怎麼同時執行這兩個檔案呢?
testbaidu1.py:https://github.com/Consini/other/blob/master/selenium/testbaidu1.py
testbaidu2.py:https://github.com/Consini/other/blob/master/selenium/testbaidu2.py
(1)addTest() 的應用
當有多個或者幾百測試用例的時候, 這樣就需要一個測試容器( 測試套件) ,把測試用例放在該容器中進行執行,unittest 模組中提供了TestSuite 類來生成測試套件,使用該類別建構函式可以生成一個測試套件的範例,該類提供了addTest來把每個測試用例加入到測試套件中。
缺點:
(2)makeSuite()和TestLoader()的應用
在unittest 框架中提供了makeSuite() 的方法,makeSuite可以實現把測試用例類內所有的測試case組成的測試套件TestSuite ,unittest 呼叫makeSuite的時候,只需要把測試類名稱傳入即可。
TestLoader 用於建立類和模組的測試套件,一般的情況下,使TestLoader().loadTestsFromTestCase(TestClass)來載入測試類。
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#匯入testbaidu1,testbaidu2
import testbaidu1
import testbaidu2
#手工新增案例到套件,
def createsuite():
suite = unittest.TestSuite()
# 將某個類的某個測試用例加入到測試容器(套件)中
# suite.addTest(testbaidu1.Baidu1("test_baidusearch"))
# suite.addTest(testbaidu1.Baidu1("test_hao"))
# suite.addTest(testbaidu2.Baidu2("test_baidusearch"))
# 把一個類裏面的所有測試方法新增到套件裏面,makeSuite可以實現把測試用例類內所有的測試case組成的測試套
# 件TestSuite ,unittest 呼叫makeSuite的時候,只需要把測試類名稱傳入即可
# suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))
# suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))
# 把一個類裏面的所有測試方法新增到套件裏面,TestLoader 用於建立類和模組的測試套件
suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)
suite = unittest.TestSuite([suite1, suite2])
return suite
if __name__=="__main__":
suite = createsuite()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
經過makeSuite()和TestLoader()的引入,我們不用一個py檔案測試類,只需要匯入一次即可。那麼能不能測試類也不用每次新增指定呢?
(3)discover()的應用
discover 是通過遞回的方式到其子目錄中從指定的目錄開始, 找到所有測試模組並返回一個包含它們物件的TestSuite ,然後進行載入與模式匹配唯一的測試檔案,discover 參數分別爲discover(dir,pattern,top_level_dir=None)
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#手工新增案例到套件
def createsuite():
discover = unittest.defaultTestLoader.discover('../selenium', pattern='test*.py', top_level_dir=None)
print(discover)
return discover
if __name__=="__main__":
suite = createsuite()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
2、用例的執行順序
unittest 框架預設載入測試用例的順序是根據ASCII 碼的順序,數位與字母的順序爲: 0~9,A~Z,a~z 。
addTest()方法按照增加順序來執行。
3、忽略用例執行
@unittest.skip("skipping")
自動化的測試中, 對於每個單獨的case來說,一個case的執行結果中, 必然會有期望結果與實際結果, 來判斷該case是通過還是失敗, 在unittest 的庫中提供了大量的實用方法來檢查預期值與實際值, 來驗證case的結果, 一般來說, 檢查條件大體分爲等價性, 邏輯比較以及其他, 如果給定的斷言通過, 測試會繼續執行到下一行的程式碼, 如果斷言失敗, 對應的case測試會立即停止或者生成錯誤資訊( 一般列印錯誤資訊即可) ,但是不要影響其他的case執行。
unittest 的單元測試庫提供了標準的xUnit 斷言方法。下面 下麪是一些常用的斷言:
#斷言,斷言失敗後下面的內容不會繼續執行
#判斷開啓的網址是否是百度
self.assertEqual(driver.title,"百度一下",msg="Not Equal")#斷言相等,結果爲false,輸出msg
self.assertEqual(driver.title,"百度一下,你就知道",msg="Not Equal")#斷言相等,結果爲true,不影響什麼
self.assertNotEqual(driver.title,"百度一下",msg="233333") #斷言不相等,結果爲true,執行成功
self.assertNotEqual(driver.title,"百度一下,你就知道",msg="233333") # 斷言不相等,結果爲false輸出msg
self.assertTrue("123"=="1234",msg="不相等")#斷言123等於1234,結果爲false,輸出:不相等
可以通過 IDE 來斷言:
指令碼執行完畢之後,還需要看到HTML報告,下面 下麪我們就通過HTMLTestRunner.py 來生成測試報告。
HTMLTestRunner支援python2.7。python3可以參見https://blog.51cto.com/hzqldjb/1590802來進行修改。
HTMLTestRunner.py 檔案,下載地址: http://tungwaiyip.info/software/HTMLTestRunner.html
下載後將其放在testcase目錄中去或者放入...\Python27\Lib 目錄下(windows)。
# -*- coding: utf-8 -*-
import unittest
import os,sys
import time
import HTMLTestRunner
#手工新增案例到套件,
def createsuite():
discover=unittest.defaultTestLoader.discover('../selenium',pattern='testba*.py',top_level_dir=None)
print(discover)
return discover
if __name__=="__main__":
# #sys.path 是python的搜尋模組的路徑集,返回結果是一個list
# curpath=sys.path[0]
#
# print(sys.path)
# print("************")
# print(sys.path[0])
#
# #取當前時間
# '''
# strftime作用是格式化時間戳爲本地時間
# time()函數用於返回當前時間的時間戳(1970年01月08時00分00秒到現在的浮點秒數)
# localtime()函數作用是格式化時間戳爲本地 struct_time
# '''
# if not os.path.exists(curpath+'/resultreport'):
# os.makedirs(curpath+'/resultreport')
# ./ 表示當前目錄
if not os.path.exists('./resultreport'):
os.mkdir('./resultreport')
now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))
# filename = curpath + '/resultreport/' + now + 'resultreport.html'
filename = './resultreport/' + now + 'resultreport.html'
with open(filename, 'wb') as fp:
# 出html報告
runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'測試報告', description=u'用例執行情況', verbosity=2)
suite = createsuite()
runner.run(suite)
用例不可能每一次執行都成功,肯定執行時候有不成功的時候。如果可以捕捉到錯誤,並且把錯誤截圖儲存,這將是一個非常棒的功能,也會給我們錯誤定位帶來方便。
例如編寫一個函數,關鍵語句爲driver.get_screenshot_as_file:
def savescreenshot(self, driver, file_name):
if not os.path.exists('./errorImage'):
os.makedirs('./errorImage')
now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
# 截圖儲存
driver.get_screenshot_as_file('./errorImage/' + now + '-' + file_name)
time.sleep(1)
一個參照的例子:https://github.com/Consini/other/blob/master/selenium/testscreenshot.py
之前我們的case都是數據和程式碼在一起編寫。考慮如下場景:
需要多次執行一個案例,比如baidu搜尋,分別輸入中文、英文、數位等進行搜尋,這時候需要編寫3個案例嗎?有沒有版本一次執行?
python 的unittest 沒有自帶數據驅動功能。所以如果使用unittest,同時又想使用數據驅動,那麼就可以使用DDT來完成。
1、ddt 的安裝
pip install ddt//安裝
pip show sst//檢查安裝是否成功
2、ddt 使用方法:參考文件:http://ddt.readthedocs.io/en/latest/
3、
testddt.py:https://github.com/Consini/other/blob/master/selenium/testddt.py
https://github.com/Consini/other/tree/master/selenium
檔案目錄解釋: