App 自动化测试中有些常见痛点问题,如果框架不能很好的解决,就可能呈现元素定位超时找不到的状况,自动化也就被打断终止了。很容易打消做自动化的激情,导致从入门到放弃。比方上面的两个问题:

一是 App 启动加载工夫较久(可能 App 自身加载慢,可能挪动设施自身加载利用速度慢,也可能首页广告工夫较长)。

另一个是各种弹框的呈现,广告弹框,降级弹框,评估弹框等。在框架中如果不能解决好下面的状况,

以雪球 App 呈现的几种弹框举例:

弹框一:

弹框二:

弹框三:

  • 弹框的影响范畴
  • 弹框对咱们自动化的影响次要是用例执行的打断,而至于弹框中广告内容的跳转或评估信息填写等属于另外的测试,因而咱们次要是要将弹框解决隐没,使利用回到用例执行的 PO;
  • 弹框的隐没形式
  • 察看弹框,咱们会发现个别为了保障用户体验,弹框都会不便用户进行一键打消,例如上述中雪球的各种弹框,可能点击一个叉号,可能任意点击其余中央,或者评估框这种间接点击“下次再说”等。
  • 弹框的解决成果
  • 自动化执行的任何时候,任意的弹框都可能呈现,在这个时候用例不能失败,须要将对应的弹框正确处理后继续执行原用例,原用例的执行过程不受影响。
  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();            }        });    }
  1. 将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); //最初调用本身实现递归,避免多弹框同时呈现造成定位失败        }}

依照下面的办法,看似曾经很好的解决了弹框的解决,然而能够留神到的是:

  • 在查看弹框的时候仍然应用的是appium的定位,在以后页面中依据元素的属性去一一查找定位
  • 所有的黑名单中的弹框都会被定位查找一遍
  • 而咱们理论中最想要的也是最有效率的办法应该是:
  • 只有在以后页面中存在的弹框才对其进行定位、操作、解决。为了达到咱们想要的成果,就须要借助于PageSource了。
  • 1)appium的driver提供了一个getPageSource办法,此办法能够在以后页面能够失去一个文本字符串,也能够了解为以后页面的xml,咱们利用这种xml文原本进行判断,就比用appium一一定位的形式要疾速和精准的多了

    String pageSource = driver.getPageSource();

2)设置黑名单,黑名单要应用元素的xpath,用来和PageSource文本做匹配,判断此弹框是否存在以后页面

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";
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,存在即依据弹框解决形式进行点击或其余操作(如上述中的新性能提醒弹框,点击弹框本身无奈打消,需点击页面其余部分方可打消)解决

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();        }    }});
//很多弹框的话,最好的是间接定位到到底哪个弹框在界面上,元素的判断应用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办法即可

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的用户信息控件,那么咱们就能够以此为根据来判断利用是否加载实现进入了首页。

在启动办法中退出显示期待上述首页控件 30 秒,到控件可被定位时确认进入首页。

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;                });

好了,通过下面的剖析之后,咱们终于搞定了入门 APP 自动化测试时的老大难问题。搞定了弹框及首页启动时加载实现如何判断解决。