常常咱們會以爲App自動化不靠譜,不穩定,其中很大的兩個緣由是:java
- App啓動加載時間較久(可能App自己加載慢,可能移動設備自己加載應用速度慢,也可能首頁廣告時間較長)。
- 各類彈框的出現;廣告彈框,升級彈框,評價彈框等。
例如以下雪球App出現的幾種彈框: android
在框架中若不對上述狀況作處理就可能出現元素定位超時找不到的報錯,自動化也就被打斷終止了,使咱們剛燃起的自動化熱情瞬間被撲滅,從入門到徹底放棄就這樣形成了。List
中,遍歷List
,經過findElements
方法獲得的List
大小來判斷彈框元素是否存在,存在即點擊處理public static void handleAlert(){
List<By> alertBox = new ArrayList<>();
alertBox.add(By.id("ib_close")); //廣告彈框
alertBox.add(By.id("md_buttonDefaultNegative")); //評價彈框
alertBox.forEach(alert->{
By adsLocator = alert;
List<WebElement> ads = driver.findElements(adsLocator);
if (ads.size() >= 1) {
ads.get(0).click();
}
});
}
複製代碼
handleAlert()
方法加到driver.findElement
方法以前,使定位前先判斷彈框的存在與否並進行處理public static WebElement findElement(By by) {
System.out.println(by);
handleAlert();
return driver.findElement(by);
}
複製代碼
上述方法初步解決了彈框問題,可是缺點也很明顯: 缺點:每次定位元素前都須要處理彈框,影響執行效率,速度較慢 所以咱們引入try catch
來解決此問題try catch
的異常捕獲處理的機制,讓元素僅在定位失敗時才進入彈框處理handleAlert()
方法,處理完畢後從新返回driver.findElement(by)
,對原case
元素繼續進行定位執行;這樣就大大提高了處理效率,使處理更爲精準public static WebElement findElement(By by) {
try {
System.out.println(by);
return driver.findElement(by);
} catch (Exception e) {
System.out.println("進入彈框處理");
handleAlert();
return driver.findElement(by);
}
}
複製代碼
findElement
方法自身,繼續進行try catch
,使之進入彈框處理邏輯public static WebElement findElement(By by) {
try {
System.out.println(by);
return driver.findElement(by);
} catch (Exception e) {
System.out.println("進入彈框處理");
handleAlert();
return findElement(by);
}
}
複製代碼
注意: 使用遞歸方法後有一個問題,就是假如並非由於某個彈框的出現而致使的定位失敗,而這個時候經過try catch
進入到彈框處理邏輯後,因爲並未匹配到彈框元素,因此遞歸就會進入一個死循環,不斷重複着彈框處理的邏輯,因此使用遞歸時咱們也須要對其次數進行限制;通常兩個彈框同時出現已經算多的了,因此建議能夠將遞歸的次數限制到最多兩次便退出。static int i = 1;
public static WebElement findElement(By by) {
try {
System.out.println(by);
return driver.findElement(by);
} catch (Exception e) {
if (i > 2){ //設置最多遞歸兩次
i = 1;
return driver.findElement(by);
}
System.out.println("進入彈框處理第"+i+"次");
handleAlert();
i++;
return findElement(by); //最後調用自身完成遞歸,防止多彈框同時出現形成定位失敗
}
}
複製代碼
按照上面的方法,看似已經很好的解決了彈框的處理,可是能夠注意到的是:app
而咱們實際中最想要的也是最有效率的方法應該是:框架
PageSource
了。1)appium
的driver
提供了一個getPageSource
方法,此方法能夠在當前頁面能夠獲得一個文本字符串,也能夠理解爲當前頁面的xml
,咱們利用這種xml
文原本進行判斷,就比用appium
一必定位的方式要快速和精準的多了測試
String pageSource = driver.getPageSource();
複製代碼
2)設置黑名單,黑名單要使用元素的xpath
,用來和PageSource
文本作匹配,判斷此彈框是否存在當前頁面lua
String adBox = "com.xueqiu.android:id/ib_close";
String gesturePromptBox = "com.xueqiu.android:id/snb_tip_text";
String evaluateBox = "com.xueqiu.android:id/md_buttonDefaultNegative";
複製代碼
3)咱們將黑名單中的xpath
做爲彈框查找的標記符
,另外再定義一個定位符
,用來找到彈框後作定位處理使用;這裏能夠將標記符
和定位符
存入HashMap
中spa
HashMap<String,By> map = new HashMap<>();
map.put(adBox,By.id("ib_close"));
map.put(gesturePromptBox,By.id("snb_tip_text"));
map.put(evaluateBox,By.id("md_buttonDefaultNegative"));
複製代碼
4)遍歷map
,判斷黑名單彈框元素是否存在於當前pageSource
,存在即根據彈框處理方式進行點擊或其餘操做(如上述中的新功能提示彈框,點擊彈框自身沒法消除,需點擊頁面其他部分方可消除)處理 code
map.entrySet().forEach(entry ->{
if (pageSource.contains(entry.getKey())){
if (entry.getKey().equals("com.xueqiu.android:id/snb_tip_text")){
System.out.println("gesturePromptBox found");
Dimension size = driver.manage().window().getSize();
//點擊屏幕的中心位置,消除新功能提示彈框
new TouchAction<>(driver).tap(PointOption.point(size.width/2,size.height/2)).perform();
}else {
//其他彈框直接點擊消除
driver.findElement(entry.getValue()).click();
}
}
});
複製代碼
5)最後有一個小技巧,臨時修改隱式等待時間,防止查找黑名單中元素太久,在彈框處理結束後再講隱式等待時間復原;完整的方法實現以下:orm
//不少彈框的話,最好的是直接定位到到底哪一個彈框在界面上,元素的判斷使用xpath
public static void handleAlertByPageSource(){
String pageSource = driver.getPageSource();//能夠獲得一個文本字符串,也能夠理解爲當前頁面的xml
//黑名單
String adBox = "com.xueqiu.android:id/ib_close";
String gesturePromptBox = "com.xueqiu.android:id/snb_tip_text";
String evaluateBox = "com.xueqiu.android:id/md_buttonDefaultNegative";
//將標記和定位符存入map
HashMap<String,By> map = new HashMap<>();
map.put(adBox,By.id("ib_close"));
map.put(gesturePromptBox,By.id("snb_tip_text"));
map.put(evaluateBox,By.id("md_buttonDefaultNegative"));
//臨時修改隱式等待時間,防止查找黑名單中元素太久
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
//遍歷map,判斷黑名單彈框元素是否存在於當前pageSource,存在即點擊處理
map.entrySet().forEach(entry ->{
if (pageSource.contains(entry.getKey())){
if (entry.getKey().equals("com.xueqiu.android:id/snb_tip_text")){
System.out.println("gesturePromptBox found");
Dimension size = driver.manage().window().getSize();
new TouchAction<>(driver).tap(PointOption.point(size.width/2,size.height/2)).perform();
}else {
driver.findElement(entry.getValue()).click();
}
}
});
//判斷完成後將隱式等待時間恢復
driver.manage().timeouts().implicitlyWait(8,TimeUnit.SECONDS);
}
複製代碼
6)最後將findElement
方法中的handleAlert
方法替換爲handleAlertByPageSource
方法便可cdn
static int i = 1;
public static WebElement findElement(By by) {
try {
System.out.println(by);
return driver.findElement(by);
} catch (Exception e) {
if (i > 2){ //設置最多遞歸兩次
i = 1;
return driver.findElement(by);
}
System.out.println("進入彈框處理第"+i+"次");
handleAlertByPageSource();
i++;
return findElement(by); //最後調用自身完成遞歸,防止多彈框同時出現形成定位失敗
}
}
複製代碼
在第一部分中介紹的首頁加載時候可能出現的坑: App啓動加載時間較久(可能App自己加載慢,可能移動設備自己加載應用速度慢,也可能首頁廣告時間較長);致使定位超時,用例失敗。 對此咱們又以下兩步解決辦法
如標題所述,對首頁進入使用顯示等待,利用搜索控件的出現來判斷是否進入了首頁,這樣不影響其餘元素隱式等待的時間,也解決了首頁初始化加載時間過長的問題;例如雪球僅在進入首頁後會出現id
爲user_profile_container
的用戶信息控件,那麼咱們就能夠以此爲依據來判斷應用是否加載完成進入了首頁。
new WebDriverWait(driver,30)
.until(ExpectedConditions.visibilityOfElementLocated(By.id("user_profile_container")));
複製代碼
缺點: 可是這樣有個狀況不能解決:若加載完成後有彈框出現,可能就一直沒法定位到首頁元素,可是實際上已經加載完成,好比下圖的首頁廣告彈框
文章第二部分介紹了利用PageSource
來判斷彈框是否存在的方法,在這裏依然適用,仍是熟悉的味道,仍是一樣的套路,將彈框元素xpath也加入PageSource判斷,這樣不管首頁控件和首頁彈框哪個被發現,就均可以判斷應用已經加載完成,成功進入首頁,剩下的就能夠交給用例和其餘處理邏輯了
new WebDriverWait(driver,30)
.until(x ->{
String xml = driver.getPageSource();
Boolean checkResult = xml.contains("user_profile_container") || xml.contains("com.xueqiu.android:id/ib_close");
System.out.println("主頁元素查找的結果是:" + checkResult);
return checkResult;
});
複製代碼