App 自动化测试中有些常见痛点问题,如果框架不能很好的解决,就可能呈现元素定位超时找不到的状况,自动化也就被打断终止了。很容易打消做自动化的激情,导致从入门到放弃。比方上面的两个问题:
一是 App 启动加载工夫较久(可能 App 自身加载慢,可能挪动设施自身加载利用速度慢,也可能首页广告工夫较长)。
另一个是各种弹框的呈现,广告弹框,降级弹框,评估弹框等。在框架中如果不能解决好下面的状况,
以雪球 App 呈现的几种弹框举例:
弹框一:
弹框二:
弹框三:
- 弹框的影响范畴
- 弹框对咱们自动化的影响次要是用例执行的打断,而至于弹框中广告内容的跳转或评估信息填写等属于另外的测试,因而咱们次要是要将弹框解决隐没,使利用回到用例执行的 PO;
- 弹框的隐没形式
- 察看弹框,咱们会发现个别为了保障用户体验,弹框都会不便用户进行一键打消,例如上述中雪球的各种弹框,可能点击一个叉号,可能任意点击其余中央,或者评估框这种间接点击“下次再说”等。
- 弹框的解决成果
- 自动化执行的任何时候,任意的弹框都可能呈现,在这个时候用例不能失败,须要将对应的弹框正确处理后继续执行原用例,原用例的执行过程不受影响。
- 将须要解决的弹框元素退出到一个黑名单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); //最初调用本身实现递归,避免多弹框同时呈现造成定位失败 }}
依照下面的办法,看似曾经很好的解决了弹框的解决,然而能够留神到的是:
- 在查看弹框的时候仍然应用的是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 自动化测试时的老大难问题。搞定了弹框及首页启动时加载实现如何判断解决。