Appium IOS 自动化测试速度优化--稳定高效的方法 (Appium 1.53 +IOS 9.1)
用Appium 做过IOS自动化的都知道,ios下的自动化是特别慢的,有时候查找一个控件居然能耗时两三分钟,试问这样的速度谁能忍受的了,有些公司为此干脆就做IOS自动化了。难道IOS的自动化就真的是这么慢么?非也,慢是因为appium的某些方法没有实现好,经过优化之后是可以摆脱这个问题的,下面给大家讲解下appium ios的优化策略。关于appium ios 速度策略优化,网上能够搜索到的好像就是只有一篇,https://testerhome.com/topics/3352,百度很多网站也是转发的这一篇文章。这篇文章说了大概的原因却没有根本的解决问题。
我们知道影响ios的自动化速度慢的原因主要就是2个方面:
1、输入操作 ,这个跟android是一样的情况
2、XPATH查找 特别耗时。
跟页面控件的多少有关,控件多的页面比如首页,就出现查找元素耗费两三分钟的情况。这主要是因为appium的xpath实现方式不好造成的,appium 实现xpath查找是先通过苹果的 UIAutomation框架 instrument JSUIATarget.localTarget().logElementTree();获取到控件树结构,再把它格式化成XML,然后再通过xpath遍历查找到控件,最后又转换为底层的instrument JS去执行操作。这其中的获取跟解析都特别耗时。
针对这两个问题,之前文章提到的方法是,尽量少用xpath定位方式,尽量减少通信,尝试使用通过driver.getPageSource()后自己重新查找方法。输入操作用setvalue代替。但是你会发现ios下的app根本现在根本没有id属性,更可恶的是有大部分的控件name,label等属性都是空的(虽然界面有文字)只能通过又丑又长的xpath表达式定位,例如:
//UIAApplication/UIAWindow/UIATableView/UIATableCell/UIAButton。这种xpath查找效率是很低的。所以虽然xpath可恨但我们还是不可避免。对于重新查找方法更不可取,一是对编程要求高,二是不知道有多少坑。对于减少通信的做法,只能适应于本地模式,如果是这种方式,不如直接用UIAutomation,用啥appium。用appium就是为了解决不同设备端的整合,分布式并发执行问题的,而这免不了通信,所以前面的文章并没有把问题根本解决。
下面给大家讲解下我的解决思路:
通过分析,我发现appium解析耗费大量时间解析xpath最终又是转换为instrument JS,为什么我们不能绕过解析xpath过程,直接把xpath翻译成instrument JS?答案是肯定的,庆幸appium提供了直接执行instrument JS的定位方式ByIosUiautomaiton.
经过优化后,ios自动化的执行速度得到了大幅提升,原来操作一个控件需要两三分钟的,也只需要2秒左右了!
具体代码实现如下:(有兴趣的可以自己去查看下instrumentJS语法)
1、xpath定位替换代码
/**
* //UIAApplication/UIAWindow/UIATableView/UIATableCell/UIAButton
* @param str
* @return
*/
privateString xpathToIosLocatorJs(String str){
String[] array=str.trim().replace("//","/").split("/");
for (int i=1;i<array.length;i++){
String tag=array;
String[] tagArray=tag.split("\\[");
String tagName=tagArray;
if (tagArray.length==2){
if (!tagName.contains("UIAApplication")&&!tagName.contains("UIAWindow")){
String indexStr=tagArray.replace("]","");
if (checkStringIsInt(indexStr)){
intindex=Integer.valueOf(indexStr)-1;
indexStr=String.valueOf(index);
tag=tag.replace("["+tagArray,"["+indexStr+"]");
}else {
indexStr="\""+indexStr.split("=").replace("\"","").replace("'","")+"\"";
tag=tag.replace("["+tagArray,"["+indexStr+"]");
}
}
}
switch (tagName){
case "UIAApplication":
tagName="frontMostApp";
break;
case "UIAWindow":
tagName="mainWindow";
break;
case "UIATableCell":
tagName="cells";
break;
case "*":
tagName="frontMostApp().mainWindow().elements";
break;
default:
tagName=tagName.replace("UIA","")+"s";
break;
}
tagName=tagName+"()";
StringBuffer sb=new StringBuffer();
if (!tagName.equals("")){
sb.append(tagName.charAt(0));
tagName=tagName.replace(tagName.charAt(0),sb.toString().toLowerCase().charAt(0));
}
tag=tag.replace(tagArray,tagName);
if (tagName.contains("frontMostApp")||tagName.contains("mainWindow")){
tag=tag.replace("["+tagArray,"");
}
str=str.replace(array,tag);
}
str=" var webElement="+str.replace("//","UIATarget.localTarget().").replace("/",".")+";";
return str;
}
private String xpathToUiautomatorJs(String str){
str=xpathToIosLocatorJs(str);
str=str.replace("//","target.").replace("/",".").replace("var webElement=","");
String[] array=str.split("\\.");
for (int i=0;i<array.length;i++){
String tagName=array;
if (tagName.contains("UIATarget")||tagName.contains("localTarget")||tagName.contains("frontMostApp")||tagName.contains("mainWindow")){
str=str.replace(array,"");
}else {
break;
}
}
str=str.substring(3,str.length()).replace(";","");
return str;
}
//获取元素
public WebElement getWebelement(Locator locator,WebDriver driver)
{
WebElement webElement;
switch (locator.getLocatorType())
{
case "xpath" :
if (driver instanceof IOSDriver){
IOSDriver iosDriver=(IOSDriver) driver;
String uiautomatorJs=xpathToUiautomatorJs(locator.getLocatorValue());
log.info("将xpath转换成uiautomatorJs"+uiautomatorJs);
webElement=iosDriver.findElementByIosUIAutomation(uiautomatorJs);
}else if (driver instanceof AndroidDriver){
String locatorValue=locator.getLocatorValue();
if (locatorValue.contains("//*[@text")){
String text=locatorValue.split("=").replace("'","").replace("]","").replace("\"","");
String uiautomatorExpress="new UiSelector().text(\""+text+"\")";
webElement=((AndroidDriver) driver).findElementByAndroidUIAutomator(uiautomatorExpress);
}else if (locatorValue.contains("//*[contains(@text")){
String text=locatorValue.split(",").replace("'","").replace("]","").replace("\"","").replace(")","");
String uiautomatorExpress="new UiSelector().textContains(\""+text+"\")";
webElement=((AndroidDriver) driver).findElementByAndroidUIAutomator(uiautomatorExpress);
}else {
webElement=driver.findElement(By.xpath(locator.getLocatorValue()));
}
}else {
webElement=driver.findElement(By.xpath(locator.getLocatorValue()));
}
break;
case "id":
webElement=driver.findElement(By.id(locator.getLocatorValue()));
break;
case "cssSelector":
webElement=driver.findElement(By.cssSelector(locator.getLocatorValue()));
break;
case "name":
webElement=driver.findElement(By.name(locator.getLocatorValue()));
break;
case "className":
webElement=driver.findElement(By.className(locator.getLocatorValue()));
break;
case "linkText":
webElement=driver.findElement(By.linkText(locator.getLocatorValue()));
break;
case "partialLinkText":
webElement=driver.findElement(By.partialLinkText(locator.getLocatorValue()));
break;
case "tagName":
webElement=driver.findElement(By.tagName(locator.getLocatorValue()));
break;
case "iosUIAutomation":
if (driver instanceof IOSDriver){
webElement=((IOSDriver) driver).findElementByIosUIAutomation(locator.getLocatorValue());
}else {
webElement=driver.findElement(By.xpath(locator.getLocatorValue()));
}
break;
default :
webElement=driver.findElement(By.xpath(locator.getLocatorValue()));
break;
}
return webElement;
}
2、input输入替代方法代码
/**
* 文本框输入操作
* @param locator元素locator
* @param value 输入值
*/
public void type(Locator locator,String value,WebDriver driver)
{
try {
WebElement webElement=findElement(locator,driver);
if (driver instanceof AppiumDriver){
if (driver instanceofAndroidDriver){
AppiumDriver androidDriver=(AppiumDriver) driver;
if (androidDriver.getContext().contains("NATIVE_APP")){
MobileElement mobileElement= (MobileElement)webElement;
mobileElement.setValue(value);
}else {
webElement.sendKeys(value);
}
}else {
MobileElement mobileElement= (MobileElement)webElement;
mobileElement.setValue(value);
}
}else {
webElement.sendKeys(value);
}
log.info("input输入:"+locator.getLocatorName()+"["+"By."+locator.getLocatorType()+":"+locator.getLocatorValue()+"value:"+value+"]");
} catch (Exception e) {
log.error("找不到元素,input输入失败:"+locator.getLocatorName()+"["+"By."+locator.getLocatorType()+":"+locator.getLocatorValue()+"]");
e.addSuppressed(new Exception(""));
e.addSuppressed(new Exception("找不到元素,input输入失败:"+getLocatorInfo(locator)));
e.printStackTrace();
throw e;
}
}原创文章,转载请注明出处
附:locator对象
/**
* Created by zhengshuheng on 2017/2/16 0016.
*/
public class Locatorimplements Serializable{
@ApiModelProperty(value = "元素Id")
private Integer locatorId;
@ApiModelProperty(value = "元素编码")
private StringlocatorNum;
@ApiModelProperty(value = "元素名称")
private StringlocatorName;
@ApiModelProperty(value = "定位方式")
private StringlocatorType;
@ApiModelProperty(value = "定位内容")
private StringlocatorValue;
@ApiModelProperty(value = "页面ID")
private Integer pageId;
//是否唯一
private Integer isOnlyOne;
public Integer getLocatorId() {
return locatorId;
}
public void setLocatorId(Integer locatorId) {
this.locatorId = locatorId;
}
public String getLocatorNum() {
return locatorNum;
}
public void setLocatorNum(String locatorNum) {
this.locatorNum = locatorNum;
}
public String getLocatorName() {
return locatorName;
}
public void setLocatorName(String locatorName) {
this.locatorName = locatorName;
}
public String getLocatorType() {
return locatorType;
}
public void setLocatorType(String locatorType) {
this.locatorType = locatorType;
}
public String getLocatorValue() {
return locatorValue;
}
public void setLocatorValue(String locatorValue) {
this.locatorValue = locatorValue;
}
public Integer getPageId() {
return pageId;
}
public void setPageId(Integer pageId) {
this.pageId = pageId;
}
public Integer getIsOnlyOne() {
return isOnlyOne 我最近就是在用instrument JS 来写IOS ui自动化,说说感受吧
1、确实快了不少,但是也没有非常非常快,和Android还是有差距,不过和直接用ID、Xpath也算欣慰了。
2、因为是跨平台的语言,所以没法获取到返回的元素对象,代码发过去以后就没以后了,也没办法try catch,或者书写更加复杂的instrument JS来控制查找到元素。appium提供的所有等待方法算是崩了。
3、我的处理方法是进入一个页面以后先用appium的方法确定所有元素就位,然后再使用instrument JS来操作元素。稳定性当然是没有appium的方法高。
4、提供一个小技巧,instrument JS也有等待的方法,但是是类似休眠的定时等待(当然还有更高级的我不会搞)UIATarget.localTarget().delay(2);如果确定能够找到页面的各项元素,可以结合等待一次性发送大量语句,效率提高很多。(稳定性你懂的) 巴黎的灯光下 发表于 2017-6-27 11:02
我最近就是在用instrument JS 来写IOS ui自动化,说说感受吧
1、确实快了不少,但是也没有非常非常快,和A ...
稳定性这一块我是用了webdrvier的webdriverwait机制,智能等待,不过即便这样ios还有一个坑,就是有时候很快找到了控件,但是文本还没有出现获取到的文本是空的,所以再这种情况下,又加了一次webdriverwait。跟Android的差距(目前用的uiautomator模式,2.0调通一次后,真机一直启动不了uiautomator2 server服务,而虚拟机却没问题,很是郁闷,也没具体对比出两者的速度)的话,感觉没什么差距用这种方法后感觉比它那个快一点,唯独就是不知道appium 审查出来的的元素,为毛ios的大部分控件是没有属性值的。
页:
[1]