這篇文章討論等待和同步的問題。過去我舉例子總是用java自帶的線程類Thread調(diào)用靜態(tài)方法sleep()來讓程序等待一會(huì)兒,目的是給網(wǎng)頁足夠的時(shí)間刷新或顯示下一個(gè)要操作的元素涮母,否則有可能元素還沒顯示出來呢但代碼已經(jīng)開始定位了挟冠。
但是,這種方式有個(gè)缺點(diǎn)巩梢,如果你代碼中需要等待的情況很多创泄,那你需要在每一個(gè)有可能出危險(xiǎn)的步驟后面寫上這句,比如你寫一個(gè)網(wǎng)上購物代碼:
而且有時(shí)候會(huì)浪費(fèi)很多沒必要等待的時(shí)間括蝠,因?yàn)椴还茉趺礃右欢〞?huì)等一個(gè)固定的時(shí)間鞠抑。比如你給它定10秒,萬一控件在上一步程序執(zhí)行后就馬上出現(xiàn)了呢忌警?那也得等10秒搁拙,沒商量,沒明白的朋友一會(huì)兒通過演示就知道了法绵。其實(shí)Thread類中還有一個(gè)方法叫wait()箕速,也是用來等待的。wait()和sleep()這兩者都屬線程類方法朋譬,控制有限盐茎,被稱為無條件同步。鑒于無條件同步的缺陷徙赢,selenium的開發(fā)人員特別設(shè)計(jì)了一個(gè)有條件同步庭呜,功能要更強(qiáng)大一些,可以更好地完成同步工作犀忱。有條件同步分隱式等待(Implicit Wait)和顯示等待(Explicit Wait)募谎。
首先是隱式等待,代碼是:
implicitlyWait()這個(gè)方法接受兩個(gè)參數(shù)阴汇,第一個(gè)是等待時(shí)間數(shù)值数冬,用整型表示;第二個(gè)是時(shí)間單位,用java中的TimeUnit類調(diào)用一些枚舉常量表示拐纱。這些枚舉常量包括時(shí)(HOURS)/分(MINUTES)/秒(SECONDS)等等铜异,調(diào)用的格式是TimeUnit.HOURS/TimeUnit.MINUTES/TimeUnit.SECONDS。這些其實(shí)java api里都寫了秸架,大家可以去看揍庄。有隱式等待參與的代碼執(zhí)行情況如下:每當(dāng)程序要找某一個(gè)或某一組元素時(shí),首先先檢查元素在不在东抹,如果在則不用執(zhí)行隱式等待語句蚂子;如果不在,先不報(bào)異常缭黔,而是等待一段給定的時(shí)間食茎,之后再次尋找,還不在則拋異常馏谨。舉個(gè)例子别渔,如果我想給它設(shè)置等待10秒,那我的代碼可以寫成:
假如我的程序里這時(shí)要找一個(gè)登錄按鈕惧互。執(zhí)行過程就是先找這個(gè)按鈕哎媚,如果沒找到不馬上拋異常,而是等10秒再檢查喊儡,再?zèng)]找到才報(bào)異常拨与。再強(qiáng)調(diào)一遍,它只作用于尋找某個(gè)或某組元素的時(shí)候管宵。大家或許已經(jīng)猜到了,也就是說攀甚,隱式等待只在findElement()和findElements()執(zhí)行時(shí)才有可能執(zhí)行箩朴。
如果你的程序里有好幾步關(guān)于尋找的代碼也沒關(guān)系,隱式等待讓所有的元素都必須按照同一種設(shè)置執(zhí)行秋度,這樣寫一遍就夠了炸庞,免去了在危險(xiǎn)步驟后都寫的麻煩。按慣例把它寫在創(chuàng)建driver之后和打開網(wǎng)頁之前:
雖然只寫一遍荚斯,但它的生命周期卻延續(xù)至整個(gè)程序執(zhí)行完畢的時(shí)候〔壕樱現(xiàn)在用示例網(wǎng)站hrsystem舉個(gè)完整的例子。我的測試步驟如下:
1. 打開網(wǎng)頁事期;
2. 輸入用戶名和密碼滥壕;
3. 點(diǎn)擊登錄按鈕;
4. 點(diǎn)擊Employee菜單兽泣;
5. 點(diǎn)擊Timesheet菜單绎橘;
6. 退出。
因?yàn)槠年P(guān)系我就不斷言了唠倦,只寫步驟称鳞。用隱式等待的寫法如下:
findElement()占了6步涮较,可隱式等待的代碼只寫了一遍。雖然只是一遍冈止,可這6步每次執(zhí)行的時(shí)候如果當(dāng)時(shí)定位不到要找的元素都會(huì)執(zhí)行一下這句狂票。但這已經(jīng)很省時(shí)間了,你執(zhí)行一下很可能就會(huì)發(fā)現(xiàn)好幾個(gè)元素出現(xiàn)時(shí)間很快熙暴,根本不用等待闺属。但如果要用無條件同步的話,你不確定哪步需要等待怨咪,只好在每步后面都寫上等待屋剑,既麻煩又浪費(fèi)時(shí)間。
有隱式就有顯式诗眨,第二種顯式等待要比隱式更高級(jí)一點(diǎn)唉匾。隱式等待只用寫一遍,由每個(gè)尋找元素的步驟共享匠楚,這本來是優(yōu)點(diǎn)巍膘,但現(xiàn)在假如是大多數(shù)元素都不用等多久,只有一個(gè)元素需要等待很長時(shí)間呢芋簿?這種情況特別討厭峡懈,但特容易遇到。只能再判斷得精確點(diǎn)了与斤。顯式等待就提供了很多種判斷方式:
這些判斷方式都是為了檢查能不能結(jié)束等待的條件肪康。顯式等待的語法如下:
第一句是初始化WebDriverWait這個(gè)類,同時(shí)設(shè)置一個(gè)等待時(shí)間撩穿。與隱式等待不一樣的是這個(gè)等待時(shí)間只能是秒磷支,不能表示小時(shí)/分鐘/毫秒等其它單位。第二句用WebDriverWait的對(duì)象調(diào)用until()方法食寡,里面接受一個(gè)參數(shù)雾狈,就是能不能結(jié)束等待的條件,ExpectedConditions就是"期望條件"的意思抵皱。我選了其中一個(gè)判斷方式visibilityOfElementLocated善榛,意思是只要被找的元素出現(xiàn)就停止等待。算上前面給的10秒呻畸,這兩句代碼的意思就是在這10秒內(nèi)不停地檢查移盆,只要元素出現(xiàn)就繼續(xù)執(zhí)行,不用非要等完這10秒伤为。如果10秒過后還不出現(xiàn)則拋異常味滞。又看出一點(diǎn)比隱式等待強(qiáng)的地方了吧?這10秒不用等完,而是只要滿足條件就繼續(xù)剑鞍,完全是動(dòng)態(tài)控制昨凡。如果要是隱式雖然有時(shí)沒必要等,可一旦真的開始等了蚁署,那也必須把給定的等待時(shí)間耗完才能繼續(xù)便脊。
當(dāng)然,顯式等待只用在耗時(shí)長的元素尋找過程中光戈,別的元素還可以使用統(tǒng)一的隱式等待哪痰。還用剛才那個(gè)test case,假如Employee菜單出來得比較慢久妆,我需要設(shè)置一個(gè)顯式等待晌杰。我把程序改一下:
我只覺得登錄時(shí)會(huì)花點(diǎn)時(shí)間,有可能會(huì)影響到下一步筷弦,所以我只在點(diǎn)擊登錄按鈕之后加上了顯式等待肋演。其他步驟還依然用隱式。大家有時(shí)間可以再用另外幾種判斷方式當(dāng)作條件試一試烂琴。
有條件同步的功能比無條件同步更強(qiáng)大爹殊,感覺也更科學(xué)。以后大家寫項(xiàng)目時(shí)可以扔掉Thread.sleep()了奸绷,改用ImplicitlyWait或ExpectedConditions梗夸。
這篇源代碼在SeleniumImplicitWait和SeleniumExplicitWait這兩個(gè)小項(xiàng)目中。
本篇知識(shí)點(diǎn)及注意事項(xiàng):
1.有隱式等待參與的代碼執(zhí)行情況如下:每當(dāng)程序要找某一個(gè)或某一組元素時(shí)号醉,首先先檢查元素在不在反症,如果在則不用執(zhí)行隱式等待語句;如果不在畔派,先不報(bào)異常铅碍,而是等待一段給定的時(shí)間,之后再次尋找父虑,還不在則拋異常该酗。隱式等待只在findElement()和findElements()執(zhí)行時(shí)才有可能執(zhí)行授药。
2.隱式等待寫在創(chuàng)建driver之后和打開網(wǎng)頁之前士嚎,其生命周期延續(xù)至整個(gè)程序執(zhí)行完畢的時(shí)候。
3.顯式等待通過用某種判斷方式設(shè)置條件這種方法來實(shí)現(xiàn)悔叽,意思是在一段給定的時(shí)間內(nèi)不停檢查條件是否滿足莱衩,一旦滿足則停止等待繼續(xù)執(zhí)行后邊的程序。超過這段時(shí)間還沒找到則拋異常娇澎。
4.顯式等待比隱式強(qiáng)大的地方有兩點(diǎn) - 1.可以對(duì)某些需要很長時(shí)間等待的元素額外設(shè)置等待時(shí)間笨蚁;2.顯式等待在給定時(shí)間內(nèi)一直在檢查條件是否滿足,只要滿足條件就停止等待繼續(xù)執(zhí)行后面代碼,完全是動(dòng)態(tài)控制括细。隱式雖然有時(shí)沒必要等伪很,可一旦真的開始等了,那也必須把給定的等待時(shí)間耗完才能繼續(xù)奋单。