Web UI自動化
1辣垒、規(guī)劃,安排
-
1.1 什么項目適合做自動化
a. 項目周期要長摔认,是否需要對這個項目進行長期維護(維優(yōu))狼荞,是否需要長期進行迭代,交付闽寡。
b. 項目團隊的能力代兵,團隊大小 -
1.2 什么時候開始做?
a. 絕大部分的功能都基本已經(jīng)穩(wěn)定了爷狈,界面植影,需求沒有太多變化了
(初期的幾個版本基本穩(wěn)定下來的之后,所以初期的幾個版本重心點都放在功能涎永,接口思币,性能等方面)
b. 哪些功能已經(jīng)穩(wěn)定了鹿响,先實現(xiàn)自動化腳本的開發(fā)
(在版本間歇期開發(fā))
1、搭建自動化測試環(huán)境
框架:python+selenium+unittest框架
-
步驟:
1谷饿、安裝Python3.x 配置環(huán)境變量
2惶我、安裝PyCharm
3、安裝測試庫
pip install selenium
? 4博投、安裝瀏覽 chrome,firefox,Ie....
? 5绸贡、安裝瀏覽器驅(qū)動
? 備注:瀏覽器驅(qū)動與瀏覽器的版本一定要匹配
? 把瀏覽器驅(qū)動chromedriver.exe放入到python的安裝目錄
2、編寫腳本
2.1毅哗、確定測試范圍:
Web自動化的覆蓋率80-90%,哪些功能有實現(xiàn)了自動化的听怕。
方維項目Web自動化
前端:注冊,登錄虑绵,首頁尿瞭,查詢標的,實名認證蒸殿,借款筷厘,投資,提現(xiàn)宏所,充值酥艳,還款
后臺:登錄,首頁爬骤,貸款管理充石,理財管理,會員管理霞玄,資金管理骤铃,
2.2、確定測試框架:
python+selenium+unittest框架
2.3坷剧、寫web UI自動化基礎腳本的思路惰爬,流程:
說明:我們其實都是通過調(diào)用selenium庫中的webdriver類中的API函數(shù)來實現(xiàn)對頁面元素進行操作,具體都是根據(jù)功能用例的操作步驟來實現(xiàn)自動化腳本的編寫惫企。并針對結(jié)果進行斷言判斷結(jié)果是否與預期結(jié)果一致撕瞧。
1、導包
2狞尔、實例化一個Webdriver對象
3丛版、加載頁面/網(wǎng)址
4、對頁面進行操作偏序,定位元素页畦,操作元素(根據(jù)功能用例中的操作步驟來的)
5、斷言—檢查結(jié)果(檢查點要全面)
備注:Web UI自動化基本腳本最核心關(guān)鍵的是:頁面元素的定位研儒,操作豫缨,還有就是斷言独令,預期結(jié)果一定要檢查到位,全面州胳。調(diào)試
#百度搜索
#用例:驗證精確搜索记焊,搜索selenium
#操作步驟:1. 輸入要搜索的數(shù)據(jù) 2. 點擊百度一下
from selenium import webdriver
import re
#1. 實例化一個Webdriver對象,打開瀏覽器
driver = webdriver.Chrome()
#2. 加載頁面
driver.get("http://www.baidu.com")
#3. 對頁面進行操作栓撞,定位元素遍膜,操作元素(根據(jù)功能用例中的操作步驟來的)
#說明:webdriver類,webdriver對象中提供了各種的API函數(shù)瓤湘,我們都是通過調(diào)用這些API函數(shù)來實現(xiàn)對頁面元素的定位
element1 = driver.find_element_by_id('kw')
element2 = driver.find_element_by_id('su')
element1.send_keys('selenium')
element2.click()
#4. 斷言
#1. 跳轉(zhuǎn)之后的頁面title應該為:selenium_百度搜索
#2. 跳轉(zhuǎn)之后的頁面的body中應該包含selenium相關(guān)的信息
try:
assert driver.title=='selenium_百度搜索','頁面title與預期結(jié)果不一致瓢颅!'
assert 'selenium' in re.findall('<body(.+?)</body>',driver.page_source)[0],'頁面的body中不存在selenium信息!'
print('百度搜索-精確搜索selenium用例,測試通過弛说!')
except AssertionError as e:
print('百度搜索-精確搜索selenium用例挽懦,測試不通過! %s' %e)
3、元素定位
3.1木人、頁面元素定位的8種基本方式:
原則:有ID盡量用id定位信柿,盡量避免用name,class name定位醒第,因為這兩個可能在壓面會有重復名字渔嚷。
? 如果沒有id,優(yōu)先選擇使用xpath,css_selector,link_text進行定位稠曼。
? 一般xpath形病,css_selector成功概率高。
-
id定位:
driver.find_element_by_id()
-
link text定位:
driver.find_element_by_link_text()
-
xpath定位:拷貝
driver.find_element_by_xpath()
-
css selector選擇器定位: 拷貝
driver.find_element_by_css_selector()
-
name名字定位:
driver.find_element_by_name()
-
class name名字定位:
driver.find_element_by_class_name()
-
tag標簽定位:
driver.find_element_by_tag_name()
-
partial_link_text模糊定位:
driver.find_element_by_partial_link_text()
#用例:驗證正常登錄用例
#功能用例的操作步驟:1. 輸入用戶名 2. 輸入密碼 3. 點擊的登錄 4. 點擊取消
#功能用例的預期結(jié)果:1. 跳轉(zhuǎn)到首頁 2. 首頁上顯示對應昵稱
import time
from selenium import webdriver
#1. 實例化一個webdriver對象
driver = webdriver.Chrome()
driver.implicitly_wait(10)
#2. 加載頁面
driver.get('http://localhost/fw')
#3. 根據(jù)功能用例的操作步驟霞幅,對頁面元素進行定位并操作
#3.1 定位用戶名輸入框漠吻,輸入用戶名
driver.find_element_by_id('login-email-address').send_keys('jason')
#3.2 定位密碼輸入框,輸入密碼
driver.find_element_by_id('login-password').send_keys('zgp123456')
#3.3 定位登錄按鈕司恳,點擊登錄按鈕
driver.find_element_by_id('Iajax-login-submit').click()
#time.sleep(1)
#3.4 定位取消按鈕途乃,點擊取消按鈕
driver.find_element_by_xpath('//*[@id="fanwe_msg_box"]/table/tbody/tr/td[2]/div[3]/input[2]').click()
time.sleep(1)
#4. 斷言,檢查實際結(jié)果與預期結(jié)果是否一致
#4.1 考慮頁面跳轉(zhuǎn)是否正常扔傅,a. 檢查頁面title b. 檢查頁面url 頁面如果沒有發(fā)生跳轉(zhuǎn)欺劳,檢查頁面上有哪些新變化,去檢查核心信息
page_element_info = driver.find_element_by_xpath('/html/body/div[2]/div/div[2]/div[1]/div/div[1]/span/span').text
print(page_element_info)
try:
assert page_element_info=='jason','頁面信息校驗失斍稹!'
print('方維登錄-正常登錄用例枫弟,測試通過邢享!')
except AssertionError as e:
print('方維登錄-正常登錄用例,測試不通過淡诗! %s' %e)
finally:
driver.close()
- xpath定位:
一般直接通過f12拷貝xpath路徑
//*[@id='login-email-address']
//*[@id="header"]/div[2]/div/ul/li[3]/a
//*[@class="ui-select-selected"]
driver.find_element_by_xpath('//*[@id="header"]/div[2]/div/ul/li[3]/div/a[3]').click() # 點擊申請貸款
#driver.find_element_by_link_text('申請借款').click()
driver.find_element_by_xpath('//*[@id="borrowlb"]/div/ul/li[2]/div[3]/a/img').click() # 點擊購房借款 (適用)
driver.find_element_by_xpath('//*[@id="J_save_deal_form"]/div[1]/div[5]/dl/dt').click() # 點擊借款用途
driver.find_element_by_xpath('//*[@class="ui-select-drop"]/a[2]').click() # 點擊購房借款
driver.find_element_by_xpath('//*[@id="borrowtitle"]').send_keys('購房首付款') # 輸入借款標題
driver.find_element_by_xpath('//*[@id="J_save_deal_form"]/div[1]/div[7]/dl/dt').click() # 點擊有無抵押
driver.find_element_by_xpath('//*[@id="J_save_deal_form"]/div[1]/div[7]/dl/dd/a[2]').click() #點擊有
- css Selector選擇器:
<input type="text" value="" name="borrowamount" id="borrowamount" class="f-input ui-textbox normal" init="init">
一個標簽元素的樣式可以使用id樣式骇塘,也可以使用class樣式,id樣式用#表示伊履,class樣式用.表示
driver.find_element_by_css_selector('#borrowamount').send_keys(5000) # 輸入借款金額
driver.find_element_by_css_selector('form#J_save_deal_form > div:nth-child(1) > div:nth-child(11) > dl > dt').click() #點擊還款周期
driver.find_element_by_css_selector('form#J_save_deal_form > div:nth-child(1) > div:nth-child(11) > dl > dd >a:nth-child(2)').click() #點擊按月還款driver.find_element_by_css_selector('#apr').send_keys(15)
driver.find_element_by_css_selector('form#J_save_deal_form > div:nth-child(1) > div:nth-child(18) > dl > dt').click() #點擊還款方式
driver.find_element_by_css_selector('form#J_save_deal_form > div:nth-child(1) > div:nth-child(18) > dl > dd >a:nth-child(3) ').click()
3.2、滾動條的處理
selenium并不是萬能的款违,有時候頁面上操作無法實現(xiàn)的唐瀑,這時候就需要借助JS來完成了
當頁面上的元素超過一屏后,想操作屏幕下方的元素插爹,是不能直接定位到哄辣,會報元素不可見的。這時候需要借助滾動條來拖動屏幕赠尾,使被操作的元素顯示在當前的屏幕上力穗。滾動條是無法直接用定位工具來定位的。selenium里面也沒有直接的方法去控制滾動條气嫁,這時候只能借助Js了当窗,還好selenium提供了一個操作js的方法:execute_script(),可以直接執(zhí)行js的腳本
一. 控制滾動條高度
1.1 滾動條回到頂部:
js="var q=document.getElementById('id').scrollTop=0"
driver.execute_script(js)
1.2 滾動條拉到底部
js="var q=document.documentElement.scrollTop=10000"
driver.execute_script(js)
1.3 滾動到指定位置
target = driver.find_element_by_id("id_keypair")
driver.execute_script("arguments[0].scrollIntoView();", target)
5寸宵、scrollTo函數(shù)
--scrollHeight 獲取對象的滾動高度崖面。
--scrollLeft 設置或獲取位于對象左邊界和窗口中目前可見內(nèi)容的最左端之間的距離。
--scrollTop 設置或獲取位于對象最頂端和窗口中可見內(nèi)容的最頂端之間的距離梯影。
--scrollWidth 獲取對象的滾動寬度巫员。
- *#滾動到底部*
js = "window.scrollTo(0,document.body.scrollHeight)"
driver.execute_script(js)
- *#滾動到頂部*
js = "window.scrollTo(0,0)"
driver.execute_script(js)
3.3、select下來列表定位:**
<select name="gpc">
<option value="stf" selected="selected">全部時間</option>
<option value="stf=1564625920.617,1564712320.617|stftype=1">最近一天</option>
<option value="stf=1564107520.617,1564712320.617|stftype=1">最近一周</option>
<option value="stf=1562033920.617,1564712320.617|stftype=1">最近一月</option>
<option value="stf=1533176320.617,1564712320.617|stftype=1">最近一年</option>
</select>
方法一:先點擊父元素光酣,彈出下來列表之后疏遏,然后在點擊子元素
driver.find_element_by_xpath('//*[@id="adv-setting-4"]/select').click()
driver.find_element_by_xpath('//*[@id="adv-setting-4"]/select/option[3]').click()
方法二:先定位父元素,再定位子元素
select_element = driver.find_element_by_xpath('//*[@id="adv-setting-4"]/select')
select_element.find_element_by_xpath('//*[@id="adv-setting-4"]/select/option[3]').click()
方法三:使用selector類
#1. 實例化一個Select對象
select = Select(driver.find_element_by_xpath('//*[@id="adv-setting-4"]/select'))
#select.select_by_value("stf=1564107520.617,1564712320.617|stftype=1")
#select.select_by_index(2)
select.select_by_visible_text('最近一周')
3.4救军、動態(tài)id:
注意:以后一旦碰到id帶有數(shù)字的财异,一般這種ID可能是動態(tài)變化的。
對于動態(tài)ID元素定位的方式不能使用ID來定位唱遭,可以使用以下幾種方式:
1戳寸、xpath路徑的方式來定位,但是這個xpath路徑不能是使用跟這個動態(tài)ID有關(guān)聯(lián)的路徑拷泽。比如:
//*[@id="img_out_306500039"] #不可以
//*[@id="qlogin_list"]/a/span[4] #xpath路徑從上一級開始進行逐級搜索疫鹊,這個是可以的。
//*[@class="img_out"] #保證class是唯一的司致,這個也可以的
2拆吆、通過css selector選擇器來定位,但css selector不能使用跟這個動態(tài)ID相關(guān)的,ID樣式
? 通過樣式脂矫,不能使用跟這個動態(tài)ID相關(guān)的,ID樣式
? 通過屬性枣耀,不能使用動態(tài)ID屬性
driver.find_element_by_css_selector(#img_out_306500039) #使用了動態(tài)ID的樣式,不可以
#driver.find_element_by_css_selector(span.img_out) #使用了class樣式庭再,可以捞奕,但是class要唯一
driver.find_element_by_css_selector(div[id="qlogin_list"]/a/span[4][class="img_out"])
3牺堰、先定位父元素,再定位子元素
parent_element = driver.find_element_by_xpath('//*[@id="qlogin_list"]/a[1]') #定位父元素
#parent_element.find_element_by_css_selector(span.img_out) #再定位子元素
3.5颅围、多窗口處理
1伟葫、如何判斷是否為一個窗口?通過獲取窗口的句柄院促,看是否有多個句柄筏养,是否有新的句柄,如果有新的句柄一疯,表示有一個新的窗口
#多窗口的處理#如何判斷是否為新的窗口
#獲取窗口的句柄(一個窗口對應一個句柄撼玄,句柄是指向這個窗口)
handles = driver.window_handles #返回的是一個列表對象
print(handles)
結(jié)果:
[
'CDwindow-4482B00295E5D9F37BD79461F3055B2A',
'CDwindow-DB98CA0F85C348ED9282D2D4F870B3B2'
]
如何跳轉(zhuǎn)到新的窗口
driver.switch_to.window(handles[1])
3.6、鼠標懸停:
在做自動化測試的時候墩邀,經(jīng)常會遇到這種情況掌猛,某個頁面元素,你必須要把鼠標移動到上面才能顯示出元素眉睹。那么這種情況荔茬,我們怎么處理呢?竹海,selenium給我們提供了一個類來處理這類事件——ActionChains慕蔚。
ActionChains可以對需要模擬鼠標操作才能進行的情況,比如單擊斋配、雙擊孔飒、點擊鼠標右鍵、拖拽等等進行操作艰争。ActionChains方法列表:
move_to_element(to_element) —— 鼠標移動到某個元素(鼠標懸停)
context_click(on_element=None) —— 點擊鼠標右鍵
double_click(on_element=None) ——雙擊鼠標左鍵
drag_and_drop(source, target) ——拖拽到某個元素然后松開
drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某個坐標然后松開
perform() —— 執(zhí)行鏈中的所有動作
ActionChains(driver).move_to_element(driver.find_element_by_xpath('//*[@id="header"]/div[2]/div/ul/li[3]/a')).perform() # 我要借款
# 點擊申請貸款
driver.find_element_by_xpath('//*[@id="header"]/div[2]/div/ul/li[3]/div/a[3]').click()
3.7坏瞄、內(nèi)嵌網(wǎng)頁的處理(iframe幀的處理)
需要先跳轉(zhuǎn)到內(nèi)嵌網(wǎng)頁中,然后在去定位元素
driver.switch_to.frame(0) #1. frame名字或id 2. frame的索引第幾個 3. 先定位這個frame元素
driver.find_element_by_xpath('/html/body').send_keys('購房借款') #輸入借款描述
先定位到iframe甩卓,然后跳轉(zhuǎn)
element = driver.find_element_by_xpath('//*[@id="J_save_deal_form"]/div[1]/div[35]/div/div/div[2]/iframe')
driver.switch_to.frame(element)
跳入iframe處理完成之后鸠匀,一定要跳出iframe
driver.switch_to.parent_frame() #跳到上一級的iframe中driver.switch_to.default_content() #跳到最外層
3.8、對話框的處理
JavaScript 有三種彈窗 Alert (只有確定按鈕), Confirmation (確定,取消等按鈕), Prompt (有輸入對話框)逾柿。
1.accept() 相當于點擊彈出框的確定按鈕:driver.switchTo().alert.accept();
2.dismiss() 相當于點擊彈出框的取消按鈕:driver.switchTo().alert.dismiss();
3.SendKeys(String input)針對于prompt情況的輸入:driver.switchTo().alert.sendKeys("可以輸入");
4.getText()獲取彈出框文本內(nèi)容:driver.switchTo().alert.getText();
4缀棍、各種元素定位異常的原因
1、頁面加載延遲導致元素定位失敗
說明:以后注意机错,凡是出現(xiàn)有點擊頁面跳轉(zhuǎn)的情況爬范,小心因為頁面延遲導致元素定位失敗的情況。
解決辦法:
1弱匪、睡覺 強制等待——不好青瀑,如果腳本中出現(xiàn)很多的sleep(1),會導致整個腳本的執(zhí)行效率。
因為:可能頁面在1s之內(nèi)早就已經(jīng)出現(xiàn)了,但是由于用的是sleep(1)狱窘,它會一直等,知道1s結(jié)束财搁。
time.sleep(1)
2蘸炸、隱式等待:
原理:如果在前面配置了隱式等待,那么只要在腳本執(zhí)行過程中尖奔,出現(xiàn)有頁面跳轉(zhuǎn)的情況搭儒,都會延遲等待頁面出現(xiàn),如果頁面一旦出現(xiàn)可以定位到元素了提茁,腳本會繼續(xù)往下執(zhí)行淹禾,而不會等到10s。如果在10s之內(nèi)還定位的元素沒有出現(xiàn)茴扁,那么會報超時異常铃岔。
隱式等待是等頁面加載,而不是元素加載G突稹;傧啊!(隱式等待就是針對頁面的卖丸,顯式等待是針對元素的纺且。)
#1. 實例化一個webdriver對象
driver = webdriver.Chrome()
driver.implicitly_wait(10) #設定隱式等待10s
3、顯示等待:
用于等待某個元素出現(xiàn)稍浆,然后再繼續(xù)執(zhí)行后續(xù)代碼载碌。顯式等待是等元素加載!P品恪嫁艇!
這里會用到一個類 WebDriverWait類
原理:總共設定時間是10s,在10s鐘之內(nèi),每隔0.5s會去找一次元素为鳄,循環(huán)找裳仆,如果在10s之內(nèi)元素還沒有出現(xiàn),則拋出異常NoSuchElementException孤钦,如果找到了歧斟,則返回找到的這個元素。然后再去執(zhí)行下一步的操作偏形。
element = WebDriverWait(driver,10).until(lambda x: x.find_element_by_xpath('//*[@id="J_save_deal_form"]/div[1]/div[7]/dl/dd/a[2]'))
element.click()#點擊有
2静袖、換其他定位方式,比如css,xpath等
3俊扭、考慮是否有iframe內(nèi)嵌網(wǎng)頁的問題
4队橙、考慮是否有多窗口問題
5、考慮是否因為滾動條位置不對,元素在頁面上看不見
6捐康、考慮是否為動態(tài)ID
5仇矾、自動化工程維護管理與優(yōu)化
1、unittest框架
unittest框架解总,Pyunit框架是一個單元測試框架贮匕,基于Python,Junit,TestNg樣式單元測試框架花枫,它基礎Java刻盐;自動化中利用unittest框架用來管理自動化用例,加載用例劳翰,執(zhí)行用例敦锌。
1.1、Unittest 核心組件
- test fixture(測試固件):
包含一個Setup()方法/函數(shù)佳簸,tearDown()方法/函數(shù)乙墙,用例執(zhí)行之前都會先執(zhí)行Setup()方法/函數(shù),主要是完成一些準備初始化的工作,比如創(chuàng)建臨時的數(shù)據(jù)庫溺蕉,文件和目錄伶丐,用例數(shù)據(jù)讀取,瀏覽器的打開等疯特,用例執(zhí)行完成之后哗魂,會執(zhí)行tearDown()方法/函數(shù),完成一些清理回收的工作漓雅,比如數(shù)據(jù)庫斷開录别,關(guān)閉瀏覽器。
(1)比如說在這個測試用例中需要訪問數(shù)據(jù)庫邻吞,那么可以在setUp()中建立數(shù)據(jù)庫連接以及進行一些初始化组题,在tearDown()中清除在數(shù)據(jù)庫中產(chǎn)生的數(shù)據(jù),然后關(guān)閉連接抱冷。注意tearDown的過程很重要崔列,要為以后的TestCase留下一個干凈的環(huán)境。
- test case(測試用例):
什么是測試用例呢旺遮?就是一個完整的測試流程,包括測試前準備環(huán)境的搭建(setUp)赵讯,以及測試后環(huán)境的還原(tearDown),還有包括用例方法耿眉,每個用例方法都必須要以test開頭
- test suite(測試套件):
多個測試用例的集合就是 suite边翼,一個 suite 可以包含多個 測試用例,也可以嵌套 suite鸣剪∽榈祝可以通過addTest()方法手動增加Test Case丈积,也可通過TestLoader自動添加Test Case,TestLoader在添加用例時债鸡,會沒有順序江滨。
- test runner(運行器):
用來執(zhí)行測試套件中測試用例的,最終執(zhí)行完成之后會生成一個測試結(jié)果厌均。
- TestLoader(加載器):用來加載用例牙寞,把用例加載到測試套件中
- Test Result(測試結(jié)果):包括運行了多少測試用例,成功了多少莫秆,失敗了多少等信息。
1.2悔详、執(zhí)行原理
1.3镊屎、unittest常用的斷言方法
assertEqual(a, b) 判斷a==b
assertNotEqual(a, b) 判斷a!=b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b) #判斷對象的茄螃,對象相對判斷
assertNotIsInstance(a, b) not isinstance(a, b) #對象不相等的判斷
1.3缝驳、如何使用unittest框架寫用例:
1、導包
import unittest
2归苍、定義一個類用狱,繼承unittest.TestCase基類
class FwLogin(unittest.TestCase):
3、重寫/覆蓋TestCase中的setUp()拼弃,tearDown()方法夏伊,在這個兩個方法中去搭建測試用例的環(huán)境,清除恢復數(shù)據(jù)
class FwLogin(unittest.TestCase):
def setUp(self):
'''
用例執(zhí)行之前都會先執(zhí)行Setup()方法/函數(shù),主要是完成一些準備初始化的工作
:return:
'''
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.driver.get('http://localhost/fw')
def tearDown(self):
'''
用例執(zhí)行完成之后吻氧,會執(zhí)行**tearDown()**方法/函數(shù)溺忧,完成一些銷毀的工作。
:return:
'''
self.driver.quit()
4盯孙、實現(xiàn)用例方法鲁森,每個用例方法必須以test開頭
class FwLogin(unittest.TestCase):
def setUp(self):
'''
用例執(zhí)行之前都會先執(zhí)行Setup()方法/函數(shù),主要是完成一些準備初始化的工作
:return:
'''
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.driver.get('http://localhost/fw')
def test_fw_normal_login(self):
'''
用例方法
:return:
'''
#1. 定位用戶名輸入框,輸入用戶名
self.driver.find_element_by_xpath('//*[@id="login-email-address"]').send_keys('jason')
#2. 定位密碼輸入框振惰,輸入密碼
self.driver.find_element_by_id('login-password').send_keys('zgp123456')
#3. 定位登錄按鈕歌溉,點擊登錄按鈕
self.driver.find_element_by_id('Iajax-login-submit').click()
#4. 定位取消按鈕,點擊取消按鈕
self.driver.find_element_by_xpath('//*[@id="fanwe_msg_box"]/table/tbody/tr/td[2]/div[3]/input[2]').click()
#5. 斷言
# 4. 斷言骑晶,檢查實際結(jié)果與預期結(jié)果是否一致
# 4.1 考慮頁面跳轉(zhuǎn)是否正常痛垛,a. 檢查頁面title b. 檢查頁面url 頁面如果沒有發(fā)生跳轉(zhuǎn),檢查頁面上有哪些新變化透罢,去檢查核心信息
page_element_info = self.driver.find_element_by_xpath(
'/html/body/div[2]/div/div[2]/div[1]/div/div[1]/span/span').text
try:
assert page_element_info == 'jason', '頁面信息校驗失敯窕蕖!'
print('方維登錄-正常登錄用例羽圃,測試通過乾胶!')
except AssertionError as e:
print('方維登錄-正常登錄用例抖剿,測試不通過! %s' % e)
def tearDown(self):
'''
用例執(zhí)行完成之后识窿,會執(zhí)行**tearDown()**方法/函數(shù)斩郎,完成一些銷毀的工作。
:return:
'''
self.driver.quit()
5喻频、利用TestLoader加載用例到測試套件中
import unittest
from test_case.fw_login import FwLogin
from test_case.fw_register import FwRegister
if __name__ == '__main__':
#加載用例
#方法1:
#1. 創(chuàng)建一個測試套件
suite = unittest.TestSuite()
#2. 加載用例到測試套件中
suite.addTest(FwLogin('test_fw_normal_login'))
suite.addTest(FwLogin('test_fw_empty_user_login'))
suite.addTest(FwRegister('test_fw_normal_register'))
#3. 創(chuàng)建一個執(zhí)行器
runner = unittest.TextTestRunner()
#4. 執(zhí)行用例
runner.run(suite)
#方法2:
# 測試套件
suite = unittest.TestSuite()
# 測試用例加載器
loader = unittest.TestLoader()
# 把測試用例加載到測試套件中
suite.addTests(loader.loadTestsFromTestCase(FwLogin))
suite.addTests(loader.loadTestsFromTestCase(FwRegister))
#3. 創(chuàng)建一個執(zhí)行器
runner = unittest.TextTestRunner()
#4. 執(zhí)行用例
runner.run(suite)
#方法3:
suite = unittest.defaultTestLoader.discover('D:\\selenium\\1947_Web_Fw_Project\\test_case\\',pattern='fw*.py')
#3. 創(chuàng)建一個執(zhí)行器
runner = unittest.TextTestRunner()
#4. 執(zhí)行用例
runner.run(suite)
6缩宜、利用TextTestRunner執(zhí)行器去執(zhí)行測試套件中的用例
#3. 創(chuàng)建一個執(zhí)行器
runner = unittest.TextTestRunner()
#4. 執(zhí)行用例
runner.run(suite)
#1. 導包
import unittest
from selenium import webdriver
#2. 定義一個類,繼承unittest.TestCase
#只有繼承了unittest.TestCase才成為了用例甥温,不繼承只是一個普通的類
class FwLogin(unittest.TestCase):
def setUp(self):
'''
用例執(zhí)行之前都會先執(zhí)行Setup()方法/函數(shù),主要是完成一些準備初始化的工作
:return:
'''
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.driver.get('http://localhost/fw')
def test_fw_normal_login(self):
'''
用例方法
:return:
'''
#1. 定位用戶名輸入框锻煌,輸入用戶名
self.driver.find_element_by_xpath('//*[@id="login-email-address"]').send_keys('jason')
#2. 定位密碼輸入框,輸入密碼
self.driver.find_element_by_id('login-password').send_keys('zgp123456')
#3. 定位登錄按鈕姻蚓,點擊登錄按鈕
self.driver.find_element_by_id('Iajax-login-submit').click()
#4. 定位取消按鈕宋梧,點擊取消按鈕
self.driver.find_element_by_xpath('//*[@id="fanwe_msg_box"]/table/tbody/tr/td[2]/div[3]/input[2]').click()
#5. 斷言
# 4. 斷言,檢查實際結(jié)果與預期結(jié)果是否一致
# 4.1 考慮頁面跳轉(zhuǎn)是否正常狰挡,a. 檢查頁面title b. 檢查頁面url 頁面如果沒有發(fā)生跳轉(zhuǎn)捂龄,檢查頁面上有哪些新變化,去檢查核心信息
page_element_info = self.driver.find_element_by_xpath(
'/html/body/div[2]/div/div[2]/div[1]/div/div[1]/span/span').text
try:
assert page_element_info == 'jason', '頁面信息校驗失敿尤倦沧!'
print('方維登錄-正常登錄用例,測試通過它匕!')
except AssertionError as e:
print('方維登錄-正常登錄用例展融,測試不通過! %s' % e)
def test_fw_empty_user_login(self):
# 1. 定位用戶名輸入框豫柬,清空用戶名文本框
self.driver.find_element_by_xpath('//*[@id="login-email-address"]').clear()
# 2. 定位密碼輸入框愈污,輸入密碼
self.driver.find_element_by_id('login-password').send_keys('zgp123456')
# 3. 定位登錄按鈕,點擊登錄按鈕
self.driver.find_element_by_id('Iajax-login-submit').click()
# 5. 斷言
assert_text = self.driver.find_element_by_xpath('//*[@id="fanwe_error_box"]/table/tbody/tr/td[2]/div[2]').text
try:
self.assertEqual('Email格式錯誤轮傍,請重新輸入或者昵稱格式錯誤暂雹,請重新輸入',assert_text,'信息不一致!')
print('方維登錄-用戶名為空用例執(zhí)行成功创夜!')
except AssertionError as e:
print('方維登錄-用戶名為空用例執(zhí)行失敽脊颉! %s' %e)
def tearDown(self):
'''
用例執(zhí)行完成之后驰吓,會執(zhí)行**tearDown()**方法/函數(shù)涧尿,完成一些銷毀的工作。
:return:
'''
self.driver.quit()
if __name__=='__main__':
unittest.main()
2檬贰、數(shù)據(jù)驅(qū)動姑廉,參數(shù)化
把用戶數(shù)據(jù)提取到Excel表格中,進行統(tǒng)一化管理翁涤,比如:就拿登錄模塊為例桥言,那用戶數(shù)據(jù)就是'用戶名'萌踱,‘密碼’,‘斷言文本’ 号阿,把這些數(shù)據(jù)提取到Excel表格中并鸵,實現(xiàn)數(shù)據(jù)與腳本的分離,然后封裝一個讀取Excel文件數(shù)據(jù)的函數(shù)實現(xiàn)數(shù)據(jù)的讀取扔涧,并利用DDT(Data Driver Test)模型講數(shù)據(jù)引用到腳本中去實現(xiàn)參數(shù)化园担。
對于Excel表格數(shù)據(jù)的讀取我們都是調(diào)用公司封裝好的模塊中的API函數(shù)來實現(xiàn)的。
對于Excel表格數(shù)據(jù)的讀取需要封裝一個模塊實現(xiàn)數(shù)據(jù)的讀取枯夜,這里需要用到xlrd,xlwt兩個庫弯汰,通過調(diào)用xlrd庫中的API來實現(xiàn)對數(shù)據(jù)的讀取,具體代碼如下:
# coding:utf-8
import xlrd
class ExcelUtil():
def __init__(self, excelPath, sheetName):
'''
構(gòu)造方法
:param excelPath:
:param sheetName:
'''
self.data = xlrd.open_workbook(excelPath)
self.table = self.data.sheet_by_name(sheetName)
# 獲取第一行作為key值
self.keys = self.table.row_values(0)
# 獲取總行數(shù)
self.rowNum = self.table.nrows
# 獲取總列數(shù)
self.colNum = self.table.ncols
def dict_data(self):
if self.rowNum <= 1:
print("總行數(shù)小于1")
else:
r = []
j=1
for i in range(self.rowNum-1):
s = {}
# 從第二行取對應values值
values = self.table.row_values(j)
for x in range(self.colNum):
s[self.keys[x]] = values[x]
r.append(s)
j+=1
return r
if __name__ == "__main__":
# filepath = "D:\\test\\web-project\\5ke\\testdata.xlsx"
filepath = "D:\\selenium\\1947_Web_Fw_Project\\data\\測試數(shù)據(jù).xls"
sheetName = "登錄"
data = ExcelUtil(filepath, sheetName)
print(data.dict_data())
引入DDT(Data Driver Test)模型湖雹,利用DDT來實現(xiàn)數(shù)據(jù)的驅(qū)動
1.安裝ddt
pip install ddt
2.導入ddt
3.在用例類上引用ddt
@ddt.ddt
class FwLogin(unittest.TestCase):
4.在測試用例函數(shù)上去引用測試數(shù)據(jù)蝙泼,這樣在測試用例函數(shù)里面就可以使用引用過來的用戶數(shù)據(jù)了。
@ddt.data(*testdata)
def test_fw_normal_login(self,data):
完整的代碼:
#1. 導包
import unittest
from selenium import webdriver
from common.readExcel import ExcelUtil
import ddt
#1. 讀取excel表格的測試數(shù)據(jù)
testdata = ExcelUtil("D:\\selenium\\1947_Web_Fw_Project\\data\\測試數(shù)據(jù).xls",'登錄').dict_data()
#[{'case_name':'正常登錄','username':'jason'劝枣,'password':'zgp123456','assert_text':'jason'},{},{},{},{}]
#2. 定義一個類,繼承unittest.TestCase
#只有繼承了unittest.TestCase才成為了用例织鲸,不繼承只是一個普通的類
@ddt.ddt
class FwLogin(unittest.TestCase):
def setUp(self):
'''
用例執(zhí)行之前都會先執(zhí)行Setup()方法/函數(shù),主要是完成一些準備初始化的工作
:return:
'''
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.driver.get('http://localhost/fw')
@ddt.data(*testdata)
def test_fw_normal_login(self,data):
'''
用例方法
:return:
'''
# print(data)
#1. 定位用戶名輸入框舔腾,輸入用戶名
self.driver.find_element_by_xpath('//*[@id="login-email-address"]').send_keys(data['username'])
#2. 定位密碼輸入框,輸入密碼
self.driver.find_element_by_id('login-password').send_keys(data['password'])
#3. 定位登錄按鈕搂擦,點擊登錄按鈕
self.driver.find_element_by_id('Iajax-login-submit').click()
if (data['case_name']=='正常登錄'):
#4. 定位取消按鈕稳诚,點擊取消按鈕
self.driver.find_element_by_xpath('//*[@id="fanwe_msg_box"]/table/tbody/tr/td[2]/div[3]/input[2]').click()
#5. 斷言
# 4. 斷言,檢查實際結(jié)果與預期結(jié)果是否一致
# 4.1 考慮頁面跳轉(zhuǎn)是否正常瀑踢,a. 檢查頁面title b. 檢查頁面url 頁面如果沒有發(fā)生跳轉(zhuǎn)扳还,檢查頁面上有哪些新變化,去檢查核心信息
if (data['case_name']=='正常登錄'):
page_element_info = self.driver.find_element_by_xpath(
'/html/body/div[2]/div/div[2]/div[1]/div/div[1]/span/span').text
#print(data['assert_text'])
#print(page_element_info)
#try:
assert page_element_info == data['assert_text'], '頁面信息校驗失敵髫病氨距!'
else:
assert_info = self.driver.find_element_by_xpath('//*[@id="fanwe_error_box"]/table/tbody/tr/td[2]/div[2]').text
self.assertEqual(assert_info,data['assert_text'],'信息不一致,斷言錯誤棘劣!')
def tearDown(self):
'''
用例執(zhí)行完成之后俏让,會執(zhí)行**tearDown()**方法/函數(shù),完成一些銷毀的工作茬暇。
:return:
'''
self.driver.quit()
if __name__=='__main__':
unittest.main()
3首昔、模塊的封裝
主要是針對一些核心的常用的功能業(yè)務模塊盡心封裝,方便調(diào)用糙俗,比如:登錄模塊勒奇,打開瀏覽器的操作,針對元素定位做二次封裝處理巧骚,數(shù)據(jù)庫的操作的封裝赊颠,Excel表格數(shù)據(jù)的讀取的封裝等等
Excel表格操作的封裝:
# coding:utf-8
import xlrd
class ExcelUtil():
def __init__(self, excelPath, sheetName):
'''
構(gòu)造方法
:param excelPath:
:param sheetName:
'''
self.data = xlrd.open_workbook(excelPath)
self.table = self.data.sheet_by_name(sheetName)
# 獲取第一行作為key值
self.keys = self.table.row_values(0)
# 獲取總行數(shù)
self.rowNum = self.table.nrows
# 獲取總列數(shù)
self.colNum = self.table.ncols
def dict_data(self):
if self.rowNum <= 1:
print("總行數(shù)小于1")
else:
r = []
j=1
for i in range(self.rowNum-1):
s = {}
# 從第二行取對應values值
values = self.table.row_values(j)
for x in range(self.colNum):
s[self.keys[x]] = values[x]
r.append(s)
j+=1
return r
if __name__ == "__main__":
# filepath = "D:\\test\\web-project\\5ke\\testdata.xlsx"
filepath = "D:\\selenium\\1947_Web_Fw_Project\\data\\測試數(shù)據(jù).xls"
sheetName = "登錄"
data = ExcelUtil(filepath, sheetName)
print(data.dict_data())
數(shù)據(jù)庫的封裝:
'''
封裝:
1. 連接數(shù)據(jù)庫
2. 增格二,刪,查巨税,改的操作蟋定。
'''
import pymysql.cursors
def connect_db(host,username,pwd,db_name,charset='utf8'):
'''
功能:連接數(shù)據(jù)庫
:param host: 主機ip
:param username: 賬號
:param pwd: 密碼
:param db_name: 數(shù)據(jù)庫名字
:param charset: 編碼方式
:return:
'''
try:
con = pymysql.connect(
host=host, # 數(shù)據(jù)庫服務器的ip地址
user=username, # 數(shù)據(jù)庫賬號
password=pwd, # 數(shù)據(jù)庫密碼
db=db_name, # 數(shù)據(jù)庫名稱
charset=charset, # 編碼方式
cursorclass=pymysql.cursors.DictCursor
)
except pymysql.err.Error as e: #捕獲異常
print("數(shù)據(jù)庫連接失敗:%s" %e)
return con # 連接對象返回出去
def close_db(connect):
'''
功能:關(guān)閉數(shù)據(jù)庫的連接
:param connect:
:return:
'''
connect.close()
def select(connect,sql,params=None):
'''
功能:查詢操作
:param connect:
:param sql:
:param params:
:return:
'''
try:
#1. 創(chuàng)建游標
crs = connect.cursor()
#2. 執(zhí)行sql語句
crs.execute(sql,params) #執(zhí)行sql語句可能會失敗
#3. 提交
connect.commit()
#4. 提取數(shù)據(jù)
result = crs.fetchall()
return result #正常返回 查詢的數(shù)據(jù)
except pymysql.err.Error as e:
print('執(zhí)行查詢操作失敗 %s' %e)
return False #如果出現(xiàn)異常,返回false
finally: #finally里面的語句一定會執(zhí)行草添。
#5. 關(guān)游標
crs.close()
def insert(connect,sql,params):
try:
# 1. 創(chuàng)建游標
crs = connect.cursor()
# 2. 執(zhí)行sql語句
crs.execute(sql, params)
# 3. 提交
connect.commit()
return True
except pymysql.err.Error as e:
print('執(zhí)行增加數(shù)據(jù)操作失敗 %s' %e)
return False
finally:
# 5. 關(guān)游標
crs.close()
def delete(connect,sql,params):
try:
# 1. 創(chuàng)建游標
crs = connect.cursor()
# 2. 執(zhí)行sql語句
crs.execute(sql, params)
# 3. 提交
connect.commit()
return True
except pymysql.err.Error as e:
print('執(zhí)行刪除數(shù)據(jù)操作失敗 %s' % e)
return False
finally:
# 5. 關(guān)游標
crs.close()
def update(connect,sql,params):
try:
# 1. 創(chuàng)建游標
crs = connect.cursor()
# 2. 執(zhí)行sql語句
crs.execute(sql, params)
# 3. 提交
connect.commit()
return True
except pymysql.err.Error as e:
print('執(zhí)行修改數(shù)據(jù)操作失敗 %s' % e)
return False
finally:
# 5. 關(guān)游標
crs.close()
打開瀏覽器的封裝:
def _open_browser(url,mode):
'''
功能:打開瀏覽器驶兜,加載頁面
:param url:
:param mode:
:return:
'''
if mode in ['Chrome','chrome']:
driver = webdriver.Chrome() #打開瀏覽器
elif mode in ['Firefox','ff','firefox']:
driver = webdriver.Firefox()
elif mode in ['Ie','ie']:
driver = webdriver.Ie()
driver.implicitly_wait(10) #設置隱式等待延遲10s
driver.maximize_window() #窗口最大化
driver.get(url) #加載頁面
return driver
登錄模塊的封裝:
#1. 登錄
def _login(driver,username,password):
# 3.1 定位用戶名輸入框,輸入用戶名
driver.find_element_by_id('login-email-address').send_keys(username)
# 3.2 定位密碼輸入框远寸,輸入密碼
driver.find_element_by_id('login-password').send_keys(password)
# 3.3 定位登錄按鈕抄淑,點擊登錄按鈕
driver.find_element_by_id('Iajax-login-submit').click()
# 3.4 定位取消按鈕,點擊取消按鈕
driver.find_element_by_xpath('//*[@id="fanwe_msg_box"]/table/tbody/tr/td[2]/div[3]/input[2]').click()
4驰后、自動化測試報告
HTMLReport是一個單元測試測試運行器肆资,可以將測試結(jié)果保存在 Html 文件中,用于人性化的結(jié)果顯示灶芝。
僅支持Python 3.x
1郑原、導包
import HTMLReport
suite = unittest.defaultTestLoader.discover('D:\\selenium\\1947_Web_Fw_Project\\test_case\\',pattern='fw*.py')
#3. 創(chuàng)建一個執(zhí)行器
# runner = unittest.TextTestRunner()
runner = HTMLReport.TestRunner(
#report_file_name='test', # 報告文件名,如果未賦值夜涕,將采用“test+時間戳”
output_path='report', # 保存文件夾名犯犁,默認“report”
title='測試報告', # 報告標題,默認“測試報告”
description='無測試描述', # 報告描述女器,默認“測試描述”
thread_count=1, # 并發(fā)線程數(shù)量(無序執(zhí)行測試)酸役,默認數(shù)量 1
thread_start_wait=3, # 各線程啟動延遲,默認 0 s
sequential_execution=False, # 是否按照套件添加(addTests)順序執(zhí)行驾胆,
# 會等待一個addTests執(zhí)行完成涣澡,再執(zhí)行下一個,默認 False
# 如果用例中存在 tearDownClass 丧诺,建議設置為True入桂,
# 否則 tearDownClass 將會在所有用例線程執(zhí)行完后才會執(zhí)行。
# lang='en'
lang='cn' # 支持中文與英文驳阎,默認中文
)
#4. 執(zhí)行用例
runner.run(suite)