做視頻網(wǎng)站視頻文件都存放在哪里網(wǎng)絡(luò)優(yōu)化工程師工作內(nèi)容
?更多關(guān)于Selenium的知識請?jiān)L問CSND論壇“蘭亭序咖啡”的專欄:
專欄《Selenium 從入門到精通》
?
目錄
目錄
需要等待的場景
自己實(shí)現(xiàn)等待邏輯
Selenium 提供的三種等待機(jī)制
隱式等待(Implicit Waits)
隱式等待的優(yōu)點(diǎn)
隱式等待的缺點(diǎn)
顯式等待(Explicit Waits)
顯式等待的優(yōu)點(diǎn)
顯式等待的缺點(diǎn)
自定義等待(Custom Waits)
自定義等待的優(yōu)點(diǎn)
自定義等待的缺點(diǎn)
總結(jié)
需要等待的場景
在 UI 自動(dòng)化時(shí),我們通常需要等待,比如以下場景:
- ?登錄后,頁面會跳轉(zhuǎn),我們需要等待頁面完全加載(否則,沒ready訪問元素會找不到)
- 點(diǎn)擊搜索按鈕后,頁面異步刷新,我們需要等待加載框消失(否則,結(jié)果還沒有update,拿到錯(cuò)誤的數(shù)據(jù))
- 頁面滿足部分條件后,預(yù)定按鈕才會可以點(diǎn)擊,我們需要等待它變成激活狀態(tài)(否則,按鈕還是disabled的狀態(tài),出現(xiàn)點(diǎn)擊報(bào)錯(cuò))
- ……
總之,這些等待,對于我們測試的穩(wěn)定性和正確性非常重要。
自己實(shí)現(xiàn)等待邏輯
最簡單的方法是,我們自己寫一個(gè)While循環(huán)不斷地檢查條件是否滿足。
while(true){if(checkCondition()){doSomeThing();}else{Thread.sleep(n秒);}
}
不過這么做不但麻煩,自己實(shí)現(xiàn)也容易出錯(cuò)。值得慶幸的是,Selenium 就提供了內(nèi)置的機(jī)制,幫助我們實(shí)現(xiàn)等待的功能。
學(xué)習(xí)中可以造輪子,幫助我們理解原理,但是生產(chǎn)項(xiàng)目中盡量用成熟的輪子。
Selenium 提供的三種等待機(jī)制
Selenium 查找元素默認(rèn)是沒有等待的,也就是說找到了就返回WebElement,否則就拋出異常。
我們測試一下,沒有配置任何等待:
@Test
public void testDefault(){WebDriver driver = null;long begin = 0;try{driver = new ChromeDriver();driver.get("https://mail.163.com");begin = System.currentTimeMillis();WebElement element = driver.findElement(By.id("inexistence"));}finally {long end = System.currentTimeMillis();log.info("花費(fèi)時(shí)間:{}毫秒", end-begin);driver.close();}
}
日志打印:-- 花費(fèi)時(shí)間:100毫秒(這個(gè)很短的時(shí)間是findElement本身執(zhí)行遍歷dom需要的一些時(shí)間)
拋出異常:
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#inexistence"}
Selenium 提供了等待機(jī)制,我們可以通過配置或者調(diào)用,很方便的實(shí)現(xiàn)我們等待的需求。
Selenium 主要分為:
- 顯式等待(Explicit Waits)
- 隱式等待(Implicit Waits)
- 自定義等待(Custom Waits)
?下面我們分別介紹它們。
隱式等待(Implicit Waits)
隱式等待是全局設(shè)置,設(shè)置一次后,對整個(gè)WebDriver實(shí)例生命周期內(nèi)的所有查找元素操作生效。
它會讓W(xué)ebdriver在尋找元素時(shí),如果元素沒有立即找到,就等待一段時(shí)間再查找,直到超過設(shè)定的最大時(shí)間或者找到元素為止。
下面是我們顯式等待的測試代碼:
@Test
public void testImplicit (){WebDriver driver = null;long begin = 0;try{driver = new ChromeDriver();// 設(shè)置隱式等待時(shí)間為10秒driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));driver.get("https://mail.163.com");WebElement element = driver.findElement(By.id("inexistence"));}finally {long end = System.currentTimeMillis();log.info("花費(fèi)時(shí)間:{}毫秒", end-begin);driver.close();}
}
其實(shí)相對于默認(rèn)的機(jī)制,我們只是加了一行配置的代碼:
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
但是效果卻非常大:
日志打印:-- 花費(fèi)時(shí)間:10129毫秒(去除方法本身的執(zhí)行時(shí)間,等待時(shí)間大于就是10秒)
拋出的異常還是找不到元素:
org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#inexistence"}
當(dāng)然不是說一定要等待這么久,如果找到了元素,會立刻返回!
等待時(shí)間只是超時(shí)時(shí)間。
隱式等待的優(yōu)點(diǎn)
簡單,一條配置,全局都有效。
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
隱式等待的缺點(diǎn)
等待的時(shí)間是全局的,但是不同元素、不同頁面、甚至不同網(wǎng)絡(luò)的加載情況不一樣,不能對每種元素設(shè)置不同的等待時(shí)間。
使用不當(dāng)?shù)脑?#xff0c;會對整個(gè)測試的效率造成非常大的影響。
比如,我們很多地方會用檢查一個(gè)元素存不存在,來判斷不同的情況。
Element ele = findElement(XXX);
if(ele == null){ // 當(dāng)然默認(rèn)找不到元素會拋異常而不是為null,不過我們可以使用findElementsdoSometing();
}else{doOtherthing();
}
如果很多地方都這樣檢查,那么花費(fèi)的時(shí)間會很長很長!
顯式等待(Explicit Waits)
上面說過,隱式等待是全局一致的,如果我們想更靈活的等待,對一些元素希望等待時(shí)間可以短一些,對另一些希望能夠多等一些時(shí)間,這時(shí)顯式等待就派上用場了。
@Test
public void testExplicitWait(){WebDriver driver = null;long begin = 0;try{driver = new ChromeDriver();driver.get("https://mail.163.com");begin = System.currentTimeMillis();// 等待id為"someId"的元素出現(xiàn)WebElement element = new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.presenceOfElementLocated(By.id("someId")));// 等待加載圖標(biāo)(class為"loading-spinner")消失new WebDriverWait(driver, Duration.ofSeconds(10)).until(ExpectedConditions.invisibilityOfElementLocated(By.className("loading-spinner")));}finally {long end = System.currentTimeMillis();log.info("花費(fèi)時(shí)間:{}毫秒", end-begin);driver.close();}
}
可以看到,我們可以對不同的元素設(shè)置不同的等待時(shí)間
new WebDriverWait(driver, java.time.Duration.ofSeconds(10))
這些wait,其實(shí)也是可以復(fù)用的,比如10s的等待的Wait,可以被用來檢查各種元素。
還能對它們設(shè)置不同的條件,比如等待元素的出現(xiàn)、消失
wait.until(ExpectedConditions.presenceOfElementLocated(By.id("someId")));
wait..until(ExpectedConditions.invisibilityOfElementLocated(By.className("loading-spinner")));
ExpectedConditions 類內(nèi)置了常用的各種等待條件,滿足我們大部分的場景:
更完整的列表,請查看這個(gè)類的方法列表
顯式等待的優(yōu)點(diǎn)
更精細(xì)更靈活的控制:
1. 對不同的元素設(shè)置不同的等待時(shí)間
2. 對不同的元素設(shè)置不同的等待條件
顯式等待的缺點(diǎn)
更復(fù)雜,維護(hù)起來也麻煩。
如果不是很熟悉,亂使用,容易適得其反。
自定義等待(Custom Waits)
顯式等待看上去很強(qiáng)大了,是不是就很完美了呢?
特殊場景下,如果你又更苛刻的要求,你也可以定制化自己的等待,進(jìn)一步的更精細(xì)的控制等待,比如:
- ?自定義重試檢查的周期
- 找不到拋異常時(shí),想根據(jù)Exception做一些處理(因?yàn)殡[式、顯式等待超時(shí)后都是直接拋出Timeout異常的,默認(rèn)不做處理往外拋)
- 定制超時(shí)后的異常消息文本
@Testpublic void test() {WebDriver driver = null;long begin = 0;try {driver = new ChromeDriver();driver.get("https://mail.163.com");begin = System.currentTimeMillis();Wait<WebDriver> wait =new FluentWait<>(driver).withTimeout(Duration.ofSeconds(2)).pollingEvery(Duration.ofMillis(300)).ignoring(ElementNotInteractableException.class);wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("no-no")));} finally {long end = System.currentTimeMillis();log.info("花費(fèi)時(shí)間:{}毫秒", end - begin);driver.close();}}
自定義等待的優(yōu)點(diǎn)
更靈活、更精細(xì)的控制
自定義等待的缺點(diǎn)
太復(fù)雜,使用前請認(rèn)真思考,我們真的需要這么細(xì)致的控制嗎?
總結(jié)
本文介紹了4種等待機(jī)制(包括默認(rèn)的),有了這些等待,可以大大的提高我們測試的準(zhǔn)確性和穩(wěn)定性。這幾種機(jī)制沒有哪個(gè)最好,我們需要根據(jù)實(shí)際的情況選擇最合適的等待。