Tech Note · 2023-01-29 1

《搬运》ui自动化 – try catch(java)/try except解决应用弹窗导致找不到坐标的异常

ui自动化 – 解决应用弹窗导致找不到坐标的异常

try catch(java)

我们利用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); //最后调用自身完成递归,防止多弹框同时出现造成定位失败
    }

}

再来解决首页加载时可能出现的坑。

启发:try finally except(python)

同样的例子也可以写成如下方式:

!/usr/bin/python

– coding: UTF-8 –

try:

fh = open("testfile", "w")
try:
    fh.write("这是一个测试文件,用于测试异常!!")
finally:
    print "关闭文件"
    fh.close()

except IOError:

print "Error: 没有找到文件或读取文件失败"

当在try块中抛出一个异常,立即执行finally块代码。
finally块中的所有语句执行后,异常被再次触发,并执行except块代码。
参数的内容不同于异常。