背景
??????項(xiàng)目是基于Ruby on Rails開(kāi)發(fā)的web程序,應(yīng)該說(shuō)項(xiàng)目中的測(cè)試實(shí)踐是很好的,具有高覆蓋率的單元測(cè)試以及比較合理的集成測(cè)試。存在的問(wèn)題是,所有的單元測(cè)試和集成測(cè)試都是針對(duì)后端代碼的,前端的JavaSript代碼沒(méi)有單元測(cè)試(這個(gè)是有歷史原因的,暫時(shí)沒(méi)法改變)。這也就意味著針對(duì)前端UI的修改是沒(méi)有底層的單元測(cè)試來(lái)保障的,只能依靠高層級(jí)的UI自動(dòng)化測(cè)試和手工測(cè)試來(lái)保障。
??????我們最近剛剛完成了一個(gè)story,是純前端的開(kāi)發(fā)工作,結(jié)果在上線后發(fā)現(xiàn)我們?cè)谛薷捻?yè)面模板文件時(shí),忘記了其他地方也在使用同樣的模板文件,導(dǎo)致了部分頁(yè)面樣式錯(cuò)亂,甚至無(wú)法訪問(wèn)。這個(gè)bug在現(xiàn)有的UI自動(dòng)化測(cè)試和手工測(cè)試中都被遺漏了,直接發(fā)布到了產(chǎn)品環(huán)境上,后來(lái)通過(guò)線上日志才被發(fā)現(xiàn)。不得已我們只好把版本先回退,修復(fù)bug后再重新上線,release的時(shí)間推遲了一周,可以說(shuō)整個(gè)團(tuán)隊(duì)為此付出了嚴(yán)重的代價(jià)。
痛點(diǎn)
? ? ??作為團(tuán)隊(duì)的QA,出現(xiàn)這樣的問(wèn)題自然讓我很不好受,顏面無(wú)光。。。拋開(kāi)沒(méi)有做story業(yè)務(wù)分析,手工測(cè)試場(chǎng)景遺漏等問(wèn)題,聚焦在現(xiàn)有的UI自動(dòng)化測(cè)試上,以下是我感知到的一些痛點(diǎn):
UI自動(dòng)化測(cè)試的穩(wěn)定性不高 - UI自動(dòng)化測(cè)試是用ruby版本的selenium實(shí)現(xiàn)的,存在著眾所周知的問(wèn)題,例如不夠穩(wěn)定,經(jīng)常由于頁(yè)面加載超時(shí),UI交互復(fù)雜等問(wèn)題導(dǎo)致測(cè)試失敗,使得自動(dòng)化測(cè)試結(jié)果的有效性不高,失去了它本來(lái)應(yīng)該具有的價(jià)值。
測(cè)試場(chǎng)景覆蓋不夠全面 - 當(dāng)然,根據(jù)測(cè)試金字塔的理念,上層的UI自動(dòng)化測(cè)試應(yīng)該是小規(guī)模的,只覆蓋基本功能:

??????但是具體到這個(gè)項(xiàng)目中,由于前端代碼的底層測(cè)試缺失,所以不得不依靠高層級(jí)的UI自動(dòng)化測(cè)試來(lái)覆蓋更多的場(chǎng)景, ? 最起碼要能完成基本的回歸測(cè)試,否則手工測(cè)試的壓力太大。這就回到了第一個(gè)問(wèn)題,正是由于UI自動(dòng)化測(cè)試的穩(wěn)定性不高,所以我只實(shí)現(xiàn)了很小一部分功能,以免后續(xù)維護(hù)的成本太高,而我們遺漏的bug恰恰沒(méi)有包含在自動(dòng)化測(cè)試的場(chǎng)景中。
問(wèn)題分析
? ? ??所以,我們的聚焦點(diǎn)集中在了如何提高UI自動(dòng)化測(cè)試的質(zhì)量上,經(jīng)過(guò)分析,主要的問(wèn)題有:
頁(yè)面加載時(shí)間超時(shí)
在用webdriver的方法訪問(wèn)頁(yè)面時(shí),經(jīng)常出現(xiàn)加載超時(shí)的問(wèn)題。頁(yè)面的DOM結(jié)構(gòu)已經(jīng)渲染完成了,但是在訪問(wèn)某些外部網(wǎng)站時(shí)長(zhǎng)時(shí)間沒(méi)有響應(yīng),導(dǎo)致測(cè)試腳本一直卡著無(wú)法繼續(xù)進(jìn)行,最后報(bào)超時(shí)錯(cuò)誤。
頁(yè)面交互action太多
UI自動(dòng)化測(cè)試的一個(gè)缺點(diǎn)就是所有的執(zhí)行動(dòng)作都要通過(guò)頁(yè)面的action來(lái)完成,例如文本框輸入,點(diǎn)擊按鈕,而且這些動(dòng)作的執(zhí)行結(jié)果存在太多的變數(shù),導(dǎo)致最后寫(xiě)出來(lái)的測(cè)試腳本太過(guò)脆弱,很容易失敗。
解決問(wèn)題
使用異常捕獲機(jī)制來(lái)處理超時(shí)等異常情況
??????針對(duì)頁(yè)面超時(shí)的問(wèn)題,由于頁(yè)面的DOM結(jié)構(gòu)已經(jīng)渲染完成了,所以其實(shí)可以繼續(xù)執(zhí)行后續(xù)的用例。但是如果與外部網(wǎng)站的請(qǐng)求沒(méi)有全部完成,selenium會(huì)認(rèn)為頁(yè)面加載沒(méi)有完成,就一直卡在訪問(wèn)頁(yè)面的get方法上。我的做法是設(shè)定一個(gè)頁(yè)面加載的超時(shí)時(shí)間:
dr.manage.timeouts.page_load = 30
??????超過(guò)30秒就認(rèn)為頁(yè)面加載超時(shí)(經(jīng)過(guò)測(cè)試,30秒的時(shí)間已經(jīng)足夠把頁(yè)面的DOM結(jié)構(gòu)渲染完成了),然后訪問(wèn)頁(yè)面時(shí)捕獲超時(shí)的異常:
begin
? ?page.open_page
rescue Selenium::WebDriver::Error::TimeOutError
???puts "ERROR:page load timeout!!"
end
??????這樣如果加載超過(guò)30秒,會(huì)拋出異常后繼續(xù)向下執(zhí)行。不致于因?yàn)樵L問(wèn)某些外部網(wǎng)站請(qǐng)求過(guò)慢,而卡在頁(yè)面加載的步驟,導(dǎo)致整個(gè)測(cè)試用例失敗。同樣的策略也可以應(yīng)用在某些復(fù)雜或者不確定的執(zhí)行結(jié)果上,捕獲可能出現(xiàn)的異常,提高測(cè)試的健壯性,避免非產(chǎn)品原因?qū)е碌臏y(cè)試用例失敗。
減少UI測(cè)試,增加API級(jí)別的測(cè)試
這個(gè)觀點(diǎn)聽(tīng)上去多少有點(diǎn)荒唐,提高UI測(cè)試質(zhì)量的方法是減少UI測(cè)試。。。。但事實(shí)就是這樣,UI測(cè)試是自動(dòng)化成本最高,最不穩(wěn)定的測(cè)試,能夠在低層級(jí)完成的測(cè)試,例如API層的測(cè)試,就不要放到UI層去完成,這樣會(huì)使測(cè)試更加穩(wěn)定和健壯。很多頁(yè)面action,例如提交表單,點(diǎn)擊按鈕等,實(shí)際都是向后臺(tái)發(fā)送了一條請(qǐng)求,如果測(cè)試的是功能,那么完全可以用API來(lái)完成這些測(cè)試。重點(diǎn)還是在于我們究竟想測(cè)什么,是否必須要從UI層去完成這些測(cè)試?這就是測(cè)試結(jié)構(gòu)的設(shè)計(jì)問(wèn)題了,這里不展開(kāi)討論。
在這個(gè)項(xiàng)目中,我把一些可以在API層完成的測(cè)試從UI測(cè)試用例中分離出來(lái),用ruby的Faraday庫(kù)(當(dāng)然也可以用其他的,例如JS的SuperTest)調(diào)用相應(yīng)的API并對(duì)返回?cái)?shù)據(jù)做校驗(yàn),這些測(cè)試相比于UI測(cè)試更加穩(wěn)定,運(yùn)行速度更快,整個(gè)測(cè)試的穩(wěn)定性便得到了提升。
將頁(yè)面交互的action與靜態(tài)頁(yè)面內(nèi)容分開(kāi)測(cè)試
這個(gè)方法的思路和上一個(gè)類似,盡量減少通過(guò)頁(yè)面加載和交互來(lái)完成的測(cè)試。如果測(cè)試內(nèi)容中不包含動(dòng)態(tài)的頁(yè)面交互步驟,例如只是想測(cè)試頁(yè)面能否正常打開(kāi),某一部分的內(nèi)容能否正常顯示等,可以從頁(yè)面的DOM結(jié)構(gòu)中通過(guò)校驗(yàn)?zāi)承┰貋?lái)完成測(cè)試。
舉個(gè)例子,如果想測(cè)試百度首頁(yè),可以不用從selenium webdriver中去加載這個(gè)頁(yè)面,直接用ruby的Faraday或者JS的SuperTest去訪問(wèn)"http://www.baidu.com"這個(gè)URL。這樣拿到的將是一段html的文本,然后再解析這段html文本(例如用ruby的Nokogirl庫(kù)),獲取對(duì)應(yīng)的內(nèi)容來(lái)做校驗(yàn)。例如返回碼是200,<title>的內(nèi)容是“百度一下,你就知道”,那么可以認(rèn)為首頁(yè)能夠正常打開(kāi)。<img>中的src屬性是一個(gè)正確的圖片文件,可以認(rèn)為百度的logo能夠正常顯示。
這里會(huì)產(chǎn)生疑問(wèn),如果我就是想測(cè)試界面怎么辦?這就回到了剛才那個(gè)問(wèn)題,我們究竟想測(cè)什么?如果只是想測(cè)功能,那么我們就盡量減少對(duì)界面的依賴。如果只是想測(cè)界面,那么也有其他的辦法來(lái)完成,例如WebdriverCSS或者PhantomCSS等界面對(duì)比的測(cè)試工具。當(dāng)然,有些測(cè)試步驟是必須要依賴頁(yè)面交互的,例如點(diǎn)擊某個(gè)按鈕打開(kāi)一個(gè)新的對(duì)話框或者跳轉(zhuǎn)到其他頁(yè)面,這些測(cè)試就只能通過(guò)webdriver來(lái)完成了。
總結(jié)
??????通過(guò)實(shí)際的嘗試,我明顯感覺(jué)到優(yōu)化后的自動(dòng)化測(cè)試相比于原來(lái)有了更穩(wěn)定的表現(xiàn),運(yùn)行速度變快,非產(chǎn)品原因?qū)е碌挠美〈螖?shù)變少。而且自動(dòng)化測(cè)試更穩(wěn)定后,我有信心去實(shí)現(xiàn)更多的自動(dòng)化測(cè)試,擴(kuò)大覆蓋的場(chǎng)景。我的一些感受和收獲是:
1.UI自動(dòng)化測(cè)試的編碼實(shí)現(xiàn)不難,難的是如何做整體的測(cè)試結(jié)構(gòu)設(shè)計(jì)。在UI測(cè)試中覆蓋的場(chǎng)景太少達(dá)不到測(cè)試的目的,場(chǎng)景太多又會(huì)成為團(tuán)隊(duì)的負(fù)擔(dān),帶來(lái)高昂的維護(hù)成本,需要和UT/API等測(cè)試綜合考慮,互相彌補(bǔ)。
2.UI自動(dòng)化測(cè)試是把雙刃劍,它應(yīng)該是QA最后考慮的自動(dòng)化測(cè)試方法。只有當(dāng)其他層級(jí)的測(cè)試無(wú)法達(dá)到目的,或者希望測(cè)試的內(nèi)容必須要通過(guò)UI完成時(shí),再去考慮用它。低層級(jí)能完成的測(cè)試不要放在高層級(jí)去完成,在靜態(tài)DOM結(jié)構(gòu)中能完成的測(cè)試不要通過(guò)webdriver加載頁(yè)面去完成。