解決App自動化的不穩定因素-彈框及首頁啓動加載完成判斷處理

一、「罪惡」源泉

常常咱們會以爲App自動化不靠譜,不穩定,其中很大的兩個緣由是:java

  • App啓動加載時間較久(可能App自己加載慢,可能移動設備自己加載應用速度慢,也可能首頁廣告時間較長)。
  • 各類彈框的出現;廣告彈框,升級彈框,評價彈框等。

例如以下雪球App出現的幾種彈框: android

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在框架中若不對上述狀況作處理就可能出現元素定位超時找不到的報錯,自動化也就被打斷終止了,使咱們剛燃起的自動化熱情瞬間被撲滅,從入門到徹底放棄就這樣形成了。

二、彈框處理

2.1 處理方向

  • 彈框的影響範圍: 彈框對咱們自動化的影響主要是用例執行的打斷,而至於彈框中廣告內容的跳轉或評價信息填寫等屬於另外的測試,所以咱們主要是要將彈框處理消失,使應用回到用例執行的PO;
  • 彈框的消失方式: 觀察彈框,咱們會發現通常爲了保證用戶體驗,彈框都會方便用戶進行一鍵消除,例如上述中雪球的各類彈框,可能點擊一個叉號,可能任意點擊其餘地方,或者評價框這種直接點擊「下次再說」等。
  • 彈框的處理效果: 自動化執行的任什麼時候候,任意的彈框均可能出現,在這個時候用例不能失敗,須要將對應的彈框正確處理後繼續執行原用例,原用例的執行過程不受影響。

2.2 處理思路與實操

2.2.1 循環判斷
  1. 將須要處理的彈框元素加入到一個黑名單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();
                }
            });
        }
    複製代碼
  2. handleAlert()方法加到driver.findElement方法以前,使定位前先判斷彈框的存在與否並進行處理
    public static WebElement findElement(By by) {
                System.out.println(by);
                handleAlert();
                return driver.findElement(by);
                }
    複製代碼
    上述方法初步解決了彈框問題,可是缺點也很明顯: 缺點:每次定位元素前都須要處理彈框,影響執行效率,速度較慢 所以咱們引入try catch來解決此問題
2.2.2 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); //最後調用自身完成遞歸,防止多彈框同時出現形成定位失敗
            }
    }
    複製代碼
2.2.3 PageSource助力效率再提高

按照上面的方法,看似已經很好的解決了彈框的處理,可是能夠注意到的是:app

  • 在檢查彈框的時候依然使用的是appium的定位,在當前頁面中根據元素的屬性去一一查找定位
  • 全部的黑名單中的彈框都會被定位查找一遍

而咱們實際中最想要的也是最有效率的方法應該是:框架

  • 只有在當前頁面中存在的彈框纔對其進行定位、操做、處理。 爲了達到咱們想要的效果,就須要藉助於PageSource了。

1)appiumdriver提供了一個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做爲彈框查找的標記符,另外再定義一個定位符,用來找到彈框後作定位處理使用;這裏能夠將標記符定位符存入HashMapspa

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自己加載慢,可能移動設備自己加載應用速度慢,也可能首頁廣告時間較長);致使定位超時,用例失敗。 對此咱們又以下兩步解決辦法

3.1 首頁元素顯示等待

如標題所述,對首頁進入使用顯示等待,利用搜索控件的出現來判斷是否進入了首頁,這樣不影響其餘元素隱式等待的時間,也解決了首頁初始化加載時間過長的問題;例如雪球僅在進入首頁後會出現iduser_profile_container的用戶信息控件,那麼咱們就能夠以此爲依據來判斷應用是否加載完成進入了首頁。

在這裏插入圖片描述

  • 在啓動方法中加入顯示等待上述首頁控件30秒,到控件可被定位時確認進入首頁
    new WebDriverWait(driver,30)
                    .until(ExpectedConditions.visibilityOfElementLocated(By.id("user_profile_container")));
    複製代碼

缺點: 可是這樣有個狀況不能解決:若加載完成後有彈框出現,可能就一直沒法定位到首頁元素,可是實際上已經加載完成,好比下圖的首頁廣告彈框

在這裏插入圖片描述

3.2 首頁彈框判斷

文章第二部分介紹了利用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;
                });
複製代碼

四、運行效果展現

在這裏插入圖片描述
相關文章
相關標籤/搜索