Appium 自动遍历工具 Java 版 (开源)

扫地僧 · 2016年05月25日 · 最后由 张天 回复于 2019年09月02日 · 9436 次阅读
本帖已被设为精华帖!

为什么重新写一个

刚才 iOS 版的也测试稳定了,顺便分享艰辛的喜悦。
两周前写过一篇文章关于深度优先的探索性遍历工具的设计和实现,写明了原因。前期调研,这方面的资料不多,感谢 testerhome 的创始人 seveniruby,已经用 scala 开发完成品,还有两篇相关的文章,给我提供了很好的借鉴,没有他前面走过的路,我必定要走很多弯路,也不可能这么快就能做出来。所以,很荣幸能进入 testerhome 这个社区,开拓了的视野。在做的过程中踩了不少坑,以每周少睡 20 多个小时的代价寻求解决方案,今天主要是说一下设计思路和技术细节。

关于开源

我相信有很多代码能力在我之上的,开源的目的是为了提供参考方案和解决思路,目前只是基础版,还请提出宝贵建议,源码传送门

关于 Appium 参数

方案一、把支持的参数都封装成 bean 读取,后来想了想觉得不够灵活,如果以后有弃用或增加的参数还要改代码;
方案二、以键值对的方式存入字典对象,遍历读取,这样就不必担心扩展了。

关于窗口唯一性

方案一、用 md5,容易受到各种干扰,特别是窗口位置的细微变化。
方案二、用 activity,普通窗口没问题,带 TAB 页的窗口就无法区分,切换 TAB 页内容不同就不要区分。
方案三、用 xpath 表达式寻求能鉴别窗口唯一性的特征元素,例如:Title。有的 app 对于控件的命名很不规范,导致很难发现共性。
方案四、也就是现在的方案,从窗口头部开始往下取几个节点(可配置),去掉坐标干扰后再生成 md5,这个方案对于窗口鉴别还算稳定。

<!--窗口鉴定策略,默认取前8个节点生成md5-->
<identify-default>8</identify-default>
<!--Tab窗口用selected区别,可能要多选几个节点到达-->
<identify-special>
    <define>专题,直播,要闻,选股>>24</define>
    <define>简单理财,投资,保险,贷款,信用卡>>30</define>
</identify-special>

关于引导流

有两种场景需要用到:
1.欢迎界面划屏至登录成功
2.app 可能由多个项目组完成,每个项目组负责一个模块,需要引导到指定模块
考虑再三,用简单的关键字驱动方式实现引导

<iosGuideFlow>
    <!--滑动类型设置-->
    <step>slide>>2</step>
    <!--点击类型设置-->
    <step>click>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAButton[1]</step>
    <step>click>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAButton[3]</step>
    <!--输入类型设置-->
    <step>input>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIATextField[1]|13012345678</step>
    <step>click>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAButton[1]</step>
    <step>input>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIASecureTextField[1]|123456</step>
    <step>click>>xpath:://UIAApplication[1]/UIAWindow[1]/UIAScrollView[1]/UIAButton[1]</step>
    <step>input>>xpath:://UIAApplication[1]/UIAWindow[3]/UIATextField[1]|8888</step>
    <step>click>>xpath:://UIAApplication[1]/UIAWindow[3]/UIAButton[2]</step>
    <!--手势密码设置-->
    <step>gesture>>xpath:://UIAButton[@name='blue circle']</step>
</iosGuideFlow>

关于提升遍历效率

这个踩的坑最多,也耗费了我大部分时间。
1.获取可操作元素
a) 预加载:获取窗口后,用类似于这种方法取到当前窗口的所有可执行元素。对于 Android 遍历影响不大,对于 IOS 遍历简直就是灾难,慢的无法接受;

List<WebElement> elementList = ((AppiumDriver) driver).findElements(By.xpath("//*[@clickable='true' and @enabled='true']"));

b) 懒加载:获取窗口后,生成所有可执行元素的 xpath,遍历出栈后再根据 xpath 获取 WebElement。

2.减少无效操作
a) 引入控件白名单机制
名单内的类才会被允许去遍历。UIAStaticText 对 iOS 来说,误杀 1% 可以减少很多不必要的遍历

<!--控件白名单-->
<click>
    <class>UIAImage</class>
    <class>UIAButton</class>
    <class>UIASwitch</class>
    <class>UIATableCell</class>
    <!--<class>UIAStaticText</class>-->
    <class>UIAPickerWheel</class>
    <class>UIACollectionCell</class>
</click>
<input>
    <class>UIATextField</class>
    <class>UIASearchBar</class>
    <class>UIASecureTextField</class>
</input>

b) 增加过滤策略
有的窗口会隐藏重复的入口,遍历工具能遍历到,这些过滤掉也能提升效率
以 iOS 为例,设计了 4 种过滤策略,不设置为默认不过滤

<!--1:id+clazz+name 2:id+clazz+name+label 3:id+clazz+name+label+value 4:no filter-->
<filter>1</filter>

c) runtime 黑名单机制,出栈的节点任务会加入黑名单列表,确保不再重复执行

关于提升稳定性

a) 黑名单机制
有些窗口想屏蔽掉,最好的办法是从根源解决,就是屏蔽入口,以 iOS 为例,支持 text,name,label 等属性的模糊匹配,xpath 精确匹配

<!--黑名单-->
<blackList>
    <item>相机</item>
    <item>相册</item>
    <item>照片</item>
    <item>退出登录</item>
    <item>拍摄名片</item>
    <item>credit card camera</item>
    <item>如您已完成添加,请重新登录</item>
    <item>重新登录</item>
    <item>//UIAApplication[1]/UIAWindow[1]/UIATableView[1]/UIAButton[1]</item>
    <item>//UIAApplication[1]/UIAWindow[1]/UIATableView[1]/UIAButton[2]</item>
    <item>//UIAApplication[1]/UIAWindow[1]/UIATableView[1]/UIAButton[3]</item>
</blackList>

b) 触发器机制
满足 xx 条件,触发 xx 操作。根据遍历中遇到的情况,支持返回、延时、点击、手势密码盘解锁。

<trigger>
     <item>分享到>>back</item>
     <item>我的权益>>delay->8</item>
     <item>温馨提示|立即开通|取消>>//UIAApplication[1]/UIAWindow[4]/UIAButton[1]</item>
     <item>更多解锁方式>>gesture->//UIAButton[@name='blue circle']</item>
 </trigger>

关于日志

日志分为系统日志和 APP 日志,系统日志又分为 info 和 error,error 的格式是固定的,便于分析出报告。
关于 APP 日志的设计
最初,如何打印日志,程序内部写死的,灵活性不高;
最终,如何打印日志,用什么工具、用哪些参数,支持用户定制化。

<log>
    <ios>idevicesyslog -u #udid#</ios>
    <android>adb -s #udid# logcat -v time -b events *:I | grep pingan</android>
</log>

关于截屏和录像

a) 截屏功能
Android 没有用 appium 的方法,直接调用命令比基于请求的效率更高;
iOS 使用 idevicescreenshot,比 appium 截图提高 10 倍效率。
b) 录像功能
把操作过程转化为流媒体,提升用户体验

关于系统参数

可定制化 Appium Server 的 port 和 host
可配置遍历深度和遍历时间
可配置截图和视频的目录

<global>
    <!--Appium port-->
    <port>5757</port>
    <!--Appium host-->
    <host>127.0.0.1</host>
    <!--测试类型 1.android 2.ios 3.web-->
    <mode>1</mode>
    <!--遍历深度-->
    <depth>0</depth>
    <!--截图和视频的目录-->
    <screenshot>/Users/mac/Desktop/png</screenshot>
    <!--遍历时间 分-->
    <duration>0</duration>
    <!--延时等待 秒-->
    <interval>3</interval>
    <!--超时 秒-->
    <timeout>30</timeout>
</global>

关于算法

之前测试用的非完整版,水平有限勿喷,主要分为节点任务执行前、执行后的处理,后进先出的结构

/**
   * 基于dfs的探索性遍历
   *
   * @param taskStack
   * @param depth
   */
  public void dfsSearch(Stack<UiNode> taskStack, int depth) {
      int thisDepth, newTaskCount, repeatCount = 0;
      UiNode thisNode;
      WebElement element;
      Stack<UiNode> children;
      Stack<UiNode> existsTaskStack;
      List<String> triggerList;
      List<UiNode> blackList = new ArrayList<>();
      String xpath, thisWindow, preWindow, thisPageSource, doBackWin;

      // 首次获取窗口内容和窗口标识
      thisPageSource = driver.getPageSource();
      thisWindow = parser.getCurrentWindowID(thisPageSource);
      preWindow = thisWindow;
      triggerList = config.getTriggerList();

      while (!taskStack.isEmpty()) {
          if (repeatCount > config.getAllowSameWinTimes())
              break;
          thisNode = taskStack.pop();
          blackList.add(thisNode);

          try {
              // 截图
              screenShot();

              // 触发器预处理
              if (triggerProcessing(driver.getPageSource(), triggerList)) {
                  screenShot();
                  thisPageSource = driver.getPageSource();
                  thisWindow = parser.getCurrentWindowID(thisPageSource);
              }

              if (!thisWindow.equals(thisNode.getWindowID())) {
                  // 在任务栈中搜索当前窗口,如果存在,则获取该窗口下所有任务节点
                  existsTaskStack = searchByWindowID(thisWindow, taskStack);

                  // 如果当前窗口已存在任务栈中
                  if (null != existsTaskStack) {
                      repeatCount = 0;
                      resetTaskStack(taskStack, existsTaskStack);
                  } else {
                      Log.logInfo(thisNode.getWindowID() + " >> " + thisWindow + ", 窗口迁移至新窗口......");
                      if (preWindow.equals(thisWindow)) {
                          repeatCount = repeatCount + 1;
                      } else {
                          repeatCount = 0;
                      }
                      preWindow = thisWindow;
                      thisDepth = thisNode.getDepth();

                      // 遍历深度控制,0表示未限制
                      if (depth == 0 || thisDepth < depth) {
                          thisPageSource = driver.getPageSource();
                          thisWindow = parser.getCurrentWindowID(thisPageSource);
                          children = getTaskStack(Type.XML, thisPageSource, thisNode.getDepth() + 1);

                          // 是否获取到新窗口节点任务
                          newTaskCount = null != children ? children.size() : 0;

                          Log.logInfo(newTaskCount + "个新任务准备入栈......");
                          children = removeNodes(blackList, children);
                          children = filterNodes(taskStack, children);
                          children = updateTaskStack(children, thisNode);

                          // 如果有新的节点任务生成,把当前节点任务先压栈,新生成的节点任务出栈
                          if (null != children && children.size() > 0) {
                              Log.logInfo(children.size() + "个新任务允许入栈......");
                              taskStack.push(thisNode);
                              taskStack.addAll(children);

                              // 更新任务栈后,新任务出栈
                              thisNode = taskStack.pop();
                              blackList.add(thisNode);
                          }

                          if (newTaskCount == 0 || children.size() == 0 && needBack(thisWindow, taskStack)) {
                              doBack();
                          }

                      } else {
                          doBack();
                      }
                  }
              }

              // 每次迭代懒加载元素对象
              xpath = thisNode.getId().split("-")[3];
              element = driver.findElement(By.xpath(xpath));
              if (thisNode.getAction().equals(Action.CLICK)) {
                  element.click();
              } else if (thisNode.getAction().equals(Action.INPUT)) {
              // todo something
              }

              // 任务执行后获取窗口内容和窗口标识
              TimeUnit.SECONDS.sleep(config.getInterval());
              thisPageSource = driver.getPageSource();
              thisWindow = parser.getCurrentWindowID(thisPageSource);

              // 如果同窗口的任务栈已处理完毕,并且还停留在该窗口,返回至上一个窗口
              if (thisNode.getWindowID().equals(thisWindow) && needBack(thisNode.getWindowID(), taskStack)) {
                  // 获取返回后的窗口内容和窗口标识
                  doBackWin = doBack();
                  thisPageSource = null == doBackWin ? thisPageSource : doBackWin;
                  thisWindow = parser.getCurrentWindowID(thisPageSource);
              }
          } catch (NoSuchElementException e) {
              continue;
          } catch (org.openqa.selenium.ElementNotVisibleException e) {
              continue;
          } catch (org.openqa.selenium.NoSuchSessionException e) {
              break;
          } catch (org.openqa.selenium.SessionNotCreatedException e) {
              break;
          } catch (org.openqa.selenium.NotFoundException e) {
              continue;
          } catch (Exception e) {
              continue;
          }
      }
  }

转载请注明作者和出处

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 157 条回复 时间 点赞

通过遍历元素对比两个版本的 ui 差异性,是否可以在源码层去做呢?毕竟 git svn 都带各个版本的 diff,拿到 diff 根据变更文件,指定变更文件扫描

—— 来自 TesterHome 官方 安卓客户端

#1 楼 @taki 这种未尝试过,自动遍历的目的不仅是 ui 差异性对比

IOS 如果控件没有 label,怎么识别控件的唯一性?@quqing 我邮箱给你了,想和你私聊一下

#3 楼 @qiangf1213 现在不能访问外网的邮箱,晚上

#4 楼 @quqing 楼主平安的啊

—— 来自 TesterHome 官方 安卓客户端

#2 楼 @quqing 那这种的目的呢?自动触发 UI 的各个 空间是否有效么?

#6 楼 @taki 我做这个的初衷是为 app 性能测试服务的,当然还有其他价值可挖掘

给你加精了, 之所以鼓励你重写. 是因为大多数人估计都学不会 scala. 行业需要一个 java 的产品.

#8 楼 @seveniruby 现在的痛点是 ios 的截图慢,大一点的要 5 秒以上

#9 楼 @quqing 是 iOS 插桩的问题. 我写了插件用 idevice 替换了截图. 效果还不错. 比原来快了一倍左右吧. 有录屏软件会更好. 截图这个挺耗费时间的. 去掉的话可以提速 5 倍以上. 但是目前录屏的软件的确都不太好用.

#10 楼 @seveniruby idevicescreenshot 用这个截图杠杠的 平均 300~400ms

我倒是对 scala 版本感兴趣,然后看了一下https://github.com/seveniruby?tab=repositories里面并没有

#12 楼 @watman scala 版本是 seveniruby 发起的项目,可以试着让他给你权限

看完你的帖子有几个问题请教下:
1、我的理解主要是 crash 监测,比 monkey 更有效,但看你说的为 APP 性能测试服务,莫非是埋点收集性能数据?
2、看了下你的核心算法,想确认下:resetTaskStack(taskStack, existsTaskStack);是将栈做更新,确保 existsTaskStack 中数据先出么?
问题有点多,非常感谢

#14 楼 @xwgoss 哦,resetTaskStack(taskStack, existsTaskStack);应该是做更新,这样确保优先点击当前页面的所有控件,应该是这么理解的
😄

#15 楼 @xwgoss 正解,不过核心算法后面有做了比较大的调整,之后 crash 也会从已有的日志里面抓取,待稳定后再开源

#16 楼 @quqing 谢谢,最近也想自己实现下 APP 自动遍历功能,冒昧问下,做较大调整的原因是什么?

#17 楼 @xwgoss 减少无效遍历,提高遍历效率,提高遍历覆盖率

请问楼主,有开源吗?

#19 楼 @jooben 过段时间会开源

请教一下遍历是只针对 GET 的操作么,如果涉及到一些表单提交等问题,是如何应对呢?

#21 楼 @cesc web 目前还不支持,有时间会做进去的

#20 楼 @quqing 好的,必须等你,谢谢呀

这两天也学了 Appium,也实现了一个简易的 Demo,代码 100 多行,没有楼主的这么系统

#24 楼 @afdsghjk 可以给你参考下,写得比较简单,一般有基础的都能看懂
https://github.com/quqing0930/AutoTraveler

#25 楼 @quqing 类似淘宝和京东这种,需要上滑屏幕来显示更多内容的,支持吗?我看源码里只解析了当前界面

ERROR running Appium command: Cannot read property 'curDeviceId' of null 这种情况怎么处理呢

xpath 识别有点问题

info: [debug] [BOOTSTRAP] [debug] Got data from client: {"cmd":"action","action":"find","params":{"strategy":"xpath","selector":"//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageButton[2]","context":"","multiple":false}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: find
info: [debug] [BOOTSTRAP] [debug] Finding //android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.RelativeLayout[1]/android.widget.RelativeLayout[1]/android.widget.ImageButton[2] using XPATH with the contextId: multiple: false
info: [debug] [BOOTSTRAP] [debug] Returning result: {"status":7,"value":"Could not find an element using supplied strategy. "}

#27 楼 @jcihain aapt 的环境变量需要设置

#28 楼 @jcihain 要看是不是无效节点,我测试都可以的,有些 node 点击无效会报元素未找到异常,不影响整体遍历,直接忽略,后续会加学习功能

#26 楼 @afdsghjk 先满足公司内部的 app,所以暂未支持,后续会加,当然也欢迎你在基础版做改进

#29 楼 @quqing 你好,设置了环境变量,还是一样。报错的地方调用的是 androidController.removeApp,请问在遍历的过程中卸载 app 的用处是?

现在的设计是遍历完毕后卸载 app,这是内部需求,后续会加开关进行控制
你按 ReadMe 里面的说明把环境配好了吗?你的报错是 Appium 报的,应该是环境问题

#31 楼 @quqing 貌似现在还发不了帖子,端午节后,把我的脚本也发出来给大家参考。用了递归,代码行数很少,刚学 Java 的都能看懂

你好,请问怎么处理登录问题,我遍历测试很多 app
的时候都会遇到需要登录的情况

#35 楼 @qinly 可以用引导流处理,文章中有描述的

深度遍历怎么保证回溯就能回到父节点,比如有些 button 点完之后只是切换 Tag 选项

#38 楼 @cryingdream94 基于深度,先进后出

@quqing hiahia~想通了,是深度。我之前也写过自动遍历工具,原打算采用 DFS 遍历,但是回溯存在一种情况回不到父节点而到了爷爷节点。

#40 楼 @cryingdream94 用任务栈重置应该可以解决

@quqing 任务栈发生重置的情况有哪些呢?

#42 楼 @cryingdream94 当前窗口和当前出栈的任务节点不一致

值得收藏

👍 值得学习

为什么我把程序打 jar 包的时候提示我

jar 打不成功

#46 楼 @304764691 问题描述清楚才能解答啊

appium 小白,对这个项目挺感兴趣的,试用了一下来遍历知乎 app。
程序不能遍历整个 app 的所有页面,日志出现 NoSuchElementException 异常,看 app 的操作像是因为页面没有后退到前一个窗口,所以无法找到元素,之后就异常退出了。
想请教一下@quqing:能帮忙看下是什么原因?是不是配置文件没有正确配置导致的?
log 输出如下:

2016-07-04 10:27:55 sys_log : [INFO] ########################################################################
2016-07-04 10:28:02 sys_log : [INFO] 遍历测试持续时间 -> 30.01s
2016-07-04 10:28:12 sys_log : [INFO] 遍历测试持续时间 -> 40.01s
2016-07-04 10:28:17 sys_log : [INFO] click -> com.zhihu.android:id/login_btn
2016-07-04 10:28:21 sys_log : [INFO] input -> com.zhihu.android:id/username, sendKeys ->xxxxxx@qq.com
2016-07-04 10:28:22 sys_log : [INFO] 遍历测试持续时间 -> 50.02s
2016-07-04 10:28:32 sys_log : [INFO] 遍历测试持续时间 -> 1.00m
2016-07-04 10:28:33 sys_log : [INFO] input -> com.zhihu.android:id/password, sendKeys -> xxxxxx
2016-07-04 10:28:42 sys_log : [INFO] click -> com.zhihu.android:id/btn_progress
2016-07-04 10:28:42 sys_log : [INFO] 遍历测试持续时间 -> 1.17m
2016-07-04 10:28:50 sys_log : [INFO] ########################### 开始执行探索性遍历测试 ###########################
2016-07-04 10:28:50 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有6个任务待运行, [android.widget.ImageButton,,,], 43caaf86fb1026d4d4072053d7468557-0,50-112,162-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.widget.ImageButton[1]
2016-07-04 10:28:51 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:28:51 sys_log : [INFO] [android.widget.ImageButton,,,], 43caaf86fb1026d4d4072053d7468557 >> 开始执行节点任务......
2016-07-04 10:28:51 sys_log : [INFO] click -> [info = android.widget.ImageButton,,,], [depth = 1]43caaf86fb1026d4d4072053d7468557-0,50-112,162-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.widget.ImageButton[1]
2016-07-04 10:28:52 sys_log : [INFO] 遍历测试持续时间 -> 1.33m
2016-07-04 10:28:54 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有5个任务待运行, [android.widget.TextView,,搜索,com.zhihu.android:id/action_search], 43caaf86fb1026d4d4072053d7468557-576,58-672,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[1]
2016-07-04 10:28:55 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:28:55 sys_log : [INFO] [android.widget.TextView,,搜索,com.zhihu.android:id/action_search], 43caaf86fb1026d4d4072053d7468557 >> 开始执行节点任务......
2016-07-04 10:28:55 sys_log : [INFO] click -> [info = android.widget.TextView,,搜索,com.zhihu.android:id/action_search], [depth = 1]43caaf86fb1026d4d4072053d7468557-576,58-672,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[1]
2016-07-04 10:29:01 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有4个任务待运行, [android.widget.TextView,,通知,com.zhihu.android:id/action_notification], 43caaf86fb1026d4d4072053d7468557-672,58-768,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[2]
2016-07-04 10:29:02 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:29:02 sys_log : [INFO] [android.widget.TextView,,通知,com.zhihu.android:id/action_notification], 43caaf86fb1026d4d4072053d7468557 >> 开始执行节点任务......
2016-07-04 10:29:02 sys_log : [INFO] click -> [info = android.widget.TextView,,通知,com.zhihu.android:id/action_notification], [depth = 1]43caaf86fb1026d4d4072053d7468557-672,58-768,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[3]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[2]
2016-07-04 10:29:02 sys_log : [INFO] 遍历测试持续时间 -> 1.50m
2016-07-04 10:29:05 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有3个任务待运行, [android.widget.FrameLayout,,,com.zhihu.android:id/fragment_paging_layout], 43caaf86fb1026d4d4072053d7468557-0,162-768,1184-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]
2016-07-04 10:29:06 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:29:06 sys_log : [INFO] [android.widget.FrameLayout,,,com.zhihu.android:id/fragment_paging_layout], 43caaf86fb1026d4d4072053d7468557 >> 开始执行节点任务......
2016-07-04 10:29:06 sys_log : [INFO] click -> [info = android.widget.FrameLayout,,,com.zhihu.android:id/fragment_paging_layout], [depth = 1]43caaf86fb1026d4d4072053d7468557-0,162-768,1184-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]
2016-07-04 10:29:12 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有2个任务待运行, [android.widget.ImageButton,,,], 43caaf86fb1026d4d4072053d7468557-608,1020-752,1180-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.view.View[1]/android.widget.ImageButton[1]
2016-07-04 10:29:12 sys_log : [INFO] 遍历测试持续时间 -> 1.67m
2016-07-04 10:29:13 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> false
2016-07-04 10:29:13 sys_log : [INFO] 43caaf86fb1026d4d4072053d7468557 >> b53af60e24e148535cfda9304fa975e4, 窗口迁移至新窗口......
2016-07-04 10:29:13 sys_log : [INFO] 相同窗口执行次数 -> 0
2016-07-04 10:29:13 sys_log : [INFO] 6个新任务准备入栈......
2016-07-04 10:29:13 sys_log : [INFO] 6个新任务允许入栈......
2016-07-04 10:29:13 sys_log : [INFO] 任务栈已更新, 6个新任务允许入栈, 现在还有9个任务待运行......
2016-07-04 10:29:13 sys_log : [INFO] 任务栈已更新,现在还有8个任务待运行......
2016-07-04 10:29:13 sys_log : [INFO] [android.widget.FrameLayout,,,com.zhihu.android:id/fragment_paging_layout], b53af60e24e148535cfda9304fa975e4 >> 开始执行节点任务......
2016-07-04 10:29:13 sys_log : [INFO] click -> [info = android.widget.FrameLayout,,,com.zhihu.android:id/fragment_paging_layout], [depth = 2]b53af60e24e148535cfda9304fa975e4-0,0-768,1184-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]
2016-07-04 10:29:17 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有7个任务待运行, [android.widget.ImageView,,,com.zhihu.android:id/avatar], b53af60e24e148535cfda9304fa975e4-48,536-128,616-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.view.View[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.ImageView[1]
2016-07-04 10:29:18 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:29:18 sys_log : [INFO] [android.widget.ImageView,,,com.zhihu.android:id/avatar], b53af60e24e148535cfda9304fa975e4 >> 开始执行节点任务......
2016-07-04 10:29:22 sys_log : [INFO] 遍历测试持续时间 -> 1.83m
2016-07-04 10:29:32 sys_log : [INFO] 遍历测试持续时间 -> 2.00m
2016-07-04 10:29:42 sys_log : [INFO] 遍历测试持续时间 -> 2.17m
2016-07-04 10:29:48 sys_log : [ERROR]   节点任务 -> [info = android.widget.ImageView,,,com.zhihu.android:id/avatar], NoSuchElementException, 弹出下一个节点任务, b53af60e24e148535cfda9304fa975e4-48,536-128,616-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.view.View[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.ImageView[1]
2016-07-04 10:29:48 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有6个任务待运行, [android.widget.LinearLayout,,,], b53af60e24e148535cfda9304fa975e4-32,1096-160,1160-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]
2016-07-04 10:29:49 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:29:49 sys_log : [INFO] [android.widget.LinearLayout,,,], b53af60e24e148535cfda9304fa975e4 >> 开始执行节点任务......
2016-07-04 10:29:52 sys_log : [INFO] 遍历测试持续时间 -> 2.33m
2016-07-04 10:30:02 sys_log : [INFO] 遍历测试持续时间 -> 2.50m
2016-07-04 10:30:12 sys_log : [INFO] 遍历测试持续时间 -> 2.67m
2016-07-04 10:30:19 sys_log : [ERROR]   节点任务 -> [info = android.widget.LinearLayout,,,], NoSuchElementException, 弹出下一个节点任务, b53af60e24e148535cfda9304fa975e4-32,1096-160,1160-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]
2016-07-04 10:30:19 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有5个任务待运行, [android.widget.ImageButton,,,], b53af60e24e148535cfda9304fa975e4-0,50-112,162-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[2]/android.widget.ImageButton[1]
2016-07-04 10:30:20 sys_log : [INFO] 当前节点任务所属窗口是否就是当前窗口 -> true
2016-07-04 10:30:20 sys_log : [INFO] [android.widget.ImageButton,,,], b53af60e24e148535cfda9304fa975e4 >> 开始执行节点任务......
2016-07-04 10:30:22 sys_log : [INFO] 遍历测试持续时间 -> 2.83m
2016-07-04 10:30:32 sys_log : [INFO] 遍历测试持续时间 -> 3.00m
2016-07-04 10:30:37 sys_log : [INFO] 开始统计异常信息...
Exception in thread "Timer-0" java.lang.ArrayIndexOutOfBoundsException: 0
    at pers.quq.filedb.tool.BytesEncodingDetect.utf16_probability(EncodingDetect.java:688)
    at pers.quq.filedb.tool.BytesEncodingDetect.detectEncoding(EncodingDetect.java:98)
    at pers.quq.filedb.tool.BytesEncodingDetect.detectEncoding(EncodingDetect.java:80)
    at pers.quq.filedb.tool.EncodingDetect.getJavaEncode(EncodingDetect.java:13)
    at pers.quq.filedb.core.FileFilterImpl.grep(FileFilterImpl.java:34)
    at pers.traveler.robot.AndroidRobot.catchAppException(AndroidRobot.java:40)
    at pers.traveler.robot.Robot.afterTravel(Robot.java:235)
    at pers.traveler.robot.Robot$1.run(Robot.java:140)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)
2016-07-04 10:30:50 sys_log : [ERROR]   节点任务 -> [info = android.widget.ImageButton,,,], 发生未知异常, 弹出下一个节点任务, b53af60e24e148535cfda9304fa975e4-0,50-112,162-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[2]/android.widget.ImageButton[1]
2016-07-04 10:30:51 sys_log : [ERROR]   org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 30.27 seconds
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 16:57:40'
System info: host: 'MACPROmatoMac-Pro.local', ip: '192.168.0.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.5', java.version: '1.8.0_65'
Driver info: io.appium.java_client.android.AndroidDriver
Capabilities [{app=/Volumes/lee-soft/mac_yang_slave/test_ws/testAppium/apps/zhihu.apk, appPackage=com.zhihu.android, networkConnectionEnabled=true, noReset=false, noSign=false, warnings={}, appiumVersion=1.0, language=zh-Hans, databaseEnabled=false, deviceName=192.168.56.101:5555, platform=LINUX, appActivity=.app.ui.activity.MainActivity, desired={app=/Volumes/lee-soft/mac_yang_slave/test_ws/testAppium/apps/zhihu.apk, appPackage=com.zhihu.android, noReset=false, noSign=false, appiumVersion=1.0, language=zh-Hans, deviceName=Mi4, appActivity=.app.ui.activity.MainActivity, autoLaunch=true, platformVersion=4.3, unicodeKeyboard=true, udid=192.168.56.101:5555, platformName=Android, resetKeyboard=true}, autoLaunch=true, platformVersion=5.1, webStorageEnabled=false, locationContextEnabled=false, browserName=Android, takesScreenshot=true, javascriptEnabled=true, unicodeKeyboard=true, udid=192.168.56.101:5555, platformName=Android, resetKeyboard=true}]
*** Element info: {Using=xpath, value=//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[2]/android.widget.ImageButton[1]}
2016-07-04 10:30:51 sys_log : [INFO] 报告主人,有1个节点任务出栈并加入黑名单 -> 现在还有4个任务待运行, [android.widget.TextView,,分享,com.zhihu.android:id/action_share], b53af60e24e148535cfda9304fa975e4-592,58-688,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[2]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[1]
2016-07-04 10:30:51 sys_log : [ERROR]   会话丢失,退出 >> 节点任务 -> [info = android.widget.TextView,,分享,com.zhihu.android:id/action_share], NoSuchSessionException, 弹出下一个节点任务, b53af60e24e148535cfda9304fa975e4-592,58-688,154-//android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.widget.FrameLayout[1]/android.support.v4.widget.DrawerLayout[1]/android.widget.FrameLayout[1]/android.widget.RelativeLayout[1]/android.view.View[1]/android.view.View[2]/android.support.v7.widget.LinearLayoutCompat[1]/android.widget.TextView[1]
Exception in thread "main" org.openqa.selenium.remote.SessionNotFoundException: Session ID is null. Using WebDriver after calling quit()?
2016-07-04 10:30:54 sys_log : [INFO] 开始创建视频......
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 16:57:40'
2016-07-04 10:30:54 sys_log : [INFO] 创建视频完毕!
System info: host: 'MACPROmatoMac-Pro.local', ip: '192.168.0.2', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.11.5', java.version: '1.8.0_65'
Driver info: driver.version: AppiumDriver
    at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:134)
    at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:64)
    at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:644)
    at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:43)
    at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:216)
    at io.appium.java_client.AppiumDriver.closeApp(AppiumDriver.java:264)
    at pers.traveler.robot.Robot.afterTravel(Robot.java:229)
    at pers.traveler.robot.AndroidRobot.working(AndroidRobot.java:88)
    at pers.traveler.robot.Robot.working(Robot.java:146)
    at pers.traveler.robot.Robot.travel(Robot.java:190)
    at pers.traveler.test.Main.main(Main.java:23)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Process finished with exit code 1

#48 楼 @jennyyung 看日志应该是配置了遍历时间是 3 分钟,时间到会结束,另:appium session 也丢失了,session 丢失我也会做退出处理。
具体原因还要看你的配置文件

好的,感谢@quqing回复,我的配置文件是这样的:

<config>
    <!--Appium 服务关键字-->
    <capability>
        <appiumVersion>1.0</appiumVersion>
        <autoLaunch>true</autoLaunch>
        <noReset>false</noReset>
        <language>zh-Hans</language>
    </capability>
    <androidCapability>
        <app>/Volumes/jenny-soft/mac_yang_slave/test_ws/testAppium/apps/zhihu.apk</app>
        <udid>192.168.56.101:5555</udid>
        <platformName>Android</platformName>
        <platformVersion>4.3</platformVersion>
        <deviceName>Mi4</deviceName>
        <appPackage>com.zhihu.android</appPackage>
        <appActivity>.app.ui.activity.MainActivity</appActivity>
        <unicodeKeyboard>true</unicodeKeyboard>
        <resetKeyboard>true</resetKeyboard>
        <noSign>false</noSign>
    </androidCapability>
    <!--引导流 关键字驱动,引导至所需模块再开始遍历-->
    <androidGuideFlow>
        <!--滑动类型设置-->
        <step>slide>>2</step>
        <!--点击类型设置-->
        <step>click>>id::com.zhihu.android:id/login_btn</step>
        <!--输入类型设置-->
        <step>input>>id::com.zhihu.android:id/username|xxxxxx@qq.com</step>
        <step>input>>id::com.zhihu.android:id/password|xxxxxx</step>
        <step>click>>id::com.zhihu.android:id/btn_progress</step>
    </androidGuideFlow>
    <rule>
        <!--窗口鉴定策略,默认取前8个节点生成md5-->
        <identify-default>8</identify-default>
        <!--Tab窗口用selected区别,可能要多选几个节点到达-->
        <identify-special>
            <define>首页,搜索,通知>>16</define>
        </identify-special>
        <!--窗口执行顺序 1:从上到下(默认) 2:从下到上-->
        <reverse>1</reverse>
        <!--控件白名单-->
        <click>
            <class>android.widget.Button</class>
            <class>android.widget.ImageButton</class>
            <class>android.widget.TextView</class>
            <class>android.widget.ImageView</class>
            <class>android.widget.FrameLayout</class>
            <class>android.widget.LinearLayout</class>
        </click>
        <input>
            <class>android.widget.EditText</class>
        </input>
        <!--启动界面提示框,包含的关键字及操作的控件,针对Android-->
        <tips>取消>>//android.widget.Button[@resource-id='android:id/button3']</tips>
        <!--无需返回的列表,需配置识别窗口唯一性的内容-->
        <notback>
            <item>首页</item>
        </notback>
        <!--黑名单 支持text,name,resourceId的过滤-->
        <blackList>
            <item>相机</item>
        </blackList>
        <!--触发器 条件>>动作 支持返回,点击,手势密码解锁,延时等待-->
        <!--过滤级别 1:clazz+text+content_desc+resourceId 2:winID+clazz+text+content_desc+resourceId 3:taskID-->
        <filter>2</filter>
        <!--退出遍历的条件:同一窗口内的停留次数-->
        <allowSameWinTimes>10</allowSameWinTimes>
    </rule>
    <!--app日志-->
    <log>
        <android>adb -s #udid# logcat -b main -b system -b events -b radio *:D | grep pingan</android>
    </log>
    <global>
        <!--Appium port-->
        <port>4723</port>
        <!--Appium host-->
        <host>127.0.0.1</host>
        <!--测试类型 1.android 2.ios 3.web-->
        <mode>1</mode>
        <!--遍历深度-->
        <depth>8</depth>
        <!--截图和视频的目录-->
        <screenshot>/Volumes/jenny-soft/zhihu/pic_output</screenshot>
        <!--遍历时间 分-->
        <duration>3</duration>
        <!--延时等待 秒-->
        <interval>3</interval>
        <!--超时 秒-->
        <timeout>30</timeout>
    </global>
</config>

#50 楼 @jennyyung 遍历时间 0 是不限制
<!--遍历时间 分-->
0

@quqing 修改了遍历时间终于跑起来了,但一旦遇到弹层提示框就无法处理直接退出了,这个已经有解决方案了吗?

#52 楼 @jennyyung 触发器、黑名单都可以,建议用触发器,满足 xx 条件,触发 xx 动作

#53 楼 @quqing 好,试试先,感谢!

xiong$ java -jar AutoTraveler.jar ios confige/ios.xml
Exception in thread "main" java.lang.NoClassDefFoundError: org/jim2mov/core/MovieSaveException
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13)
at pers.traveler.test.Main.main(Main.java:23)
Caused by: java.lang.ClassNotFoundException: org.jim2mov.core.MovieSaveException
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more

这个是我环境没配好吗

#55 楼 @304764691 类没找到,这个 jar 包有点老了,最近更新过源码,git 源码试试吧

#56 楼 @quqing xiong$ java -jar AutoTraveler.jar ios config/ios.xml
Exception in thread "main" java.lang.NoClassDefFoundError: pers/quq/filedb/core/Filter
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13)
at pers.traveler.test.TravelerTest.main(TravelerTest.java:16)
Caused by: java.lang.ClassNotFoundException: pers.quq.filedb.core.Filter
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more

我更新了还是一样 是不是我打包打的有问题啊

#57 楼 @304764691 filedb 是我写的一个插件,都放里面了,应该是打包问题

#58 楼 @quqing Exception in thread "main" java.lang.NoClassDefFoundError: org/openqa/selenium/Capabilities
现在是这个提示 大神能邮件 我能问问问题 我基础比较差 304764691@qq.com

#59 楼 @304764691 你应该是 IDE 没用好,这几个问题都是 jar 包没引入导致的,这个没法手把手教,google 吧

这遍历太耗能了

—— 来自 TesterHome 官方 安卓客户端

—— 来自 TesterHome 官方 安卓客户端

@quqing 通过 eclipse 和 gradle 以及 jar 命令,打出来的 jar 文件都不能使用。
求提供一个可用的 jar 文件试用。谢谢
其他朋友要是有的话,也感谢提供。😀

执行:java -jar AutoTraveler.jar ios confige/ios.xml
terminal 显示:AutoTraveler.jar 中没有主清单属性

@jennyyung 刚试用了下你的配置遍历知乎,出现了个问题是页面跳转后,当前节点任务所属窗口还是当前窗口,窗口没更新导致无法运行下去。

现在发现一个问题,提个 issue,
应该把 xml 里面的&# 过滤掉
有些 app 里面不可避免有 emoji。emoji 正则表达式包含了这样的&# 非法字符,需要过滤

这得要多少信息量啊

#65 楼 @zengjunzhou 这边没遇到过这种问题,是不是堆栈中有很多无效节点造成的呢,配置文件有默认值处理的逻辑
<!--退出遍历的条件:同一窗口内的停留次数-->
10

#66 楼 @addison 谢谢提出宝贵意见

@quqing 经调试,是默认的窗口鉴定策略没配置好,没有使用 identify-special, 使用 identify-default,原配置对比的值 8 太小,很多页面前 8 个元素都相同导致窗口 id 返回值相同,最后索性传了整个 driver.getPageSource() 进去 md5 加密得以解决。遇到新的问是遍历过程中出现退回到桌面的情况。

#70 楼 @zengjunzhou 这个配置里面对不需要返回的窗口有规则支持(窗口内容对规则内所有的关键字都包含才会命中),

首页,财经快讯,我,我的资产,财富加速,财富排名,健康,房产
首页,财经快讯,我,我的订单,活期,定期
首页,财经快讯,我,直播,要闻,专题,选股
首页,财经快讯,我,账户余额,银行卡,订单中心,我的信用

@quqing,开始设置遍历深度 3 出现退回桌面情况,加大遍历深度后遍历效果有所改善,目前还没出现过返回桌面情况。建议加个包名判断,如果包名变化,新界面的节点不入任务栈并返回。

m

—— 来自 TesterHome 官方 安卓客户端

楼主请问 idevicescreenshot 这是怎么用的,我有项目用 appium 截图性能太差了,有没有源码参考一下,感谢!

#74 楼 @heyyuyu 已经开源了,文章开头有传送门

#75 楼 @quqing 希望早点更新 code 解决下&# 的问题 谢谢 quqing~

#75 楼 @quqing 源码看了,只提到用 Runtime 调用 idevicescreenshot,楼主应该是在电脑安装了这个程序,请问有 idevicescreenshot 的具体链接吗?

#77 楼 @heyyuyu 链接我也记不住了,你到 github 上去搜一下吧

#76 楼 @addison 请把有问题的 xml 结构发我一份,邮箱:quqing0930@126.com

#79 楼 @quqing 我传你 QQ 吧 我有你 QQ😁 行嘛

#79 楼 @quqing 感谢楼主提供了一个很棒的截图工具,截图时间降低到 1 秒,完美解决!

#80 楼 @addison 393472146

#81 楼 @heyyuyu 求截图工具传送门😃

#84 楼 @heyyuyu 非常感谢,只 For IOS?有没有 Android 平台的?

#85 楼 @victors 只是 iOS 的,安卓不需要用这个

#13 楼 @quqing quqing 你好,请问你的这个版本核心遍历算法部分用的是 DFS 和 Scala 版本的处理方式一样么,目前那版还未开源,反编效果也不好,想研究一下。请指教,谢谢。

#87 楼 @victors 我没仔细看过 scala 版本,思想应该大同小异,具体实现方式肯定有区别的

#88 楼 @quqing

Exception in thread "main" java.lang.UnsupportedClassVersionError: pers/quq/filedb/core/Filter : Unsupported major.minor version 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at java.security.SecureClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.defineClass(Unknown Source)
    at java.net.URLClassLoader.access$100(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13)
    at pers.traveler.test.TravelerTest.main(TravelerTest.java:16)

我在 github 上拿到源码,一直在看,还没有跑成功,报错如上
Windows7
android 6.0
测试 Apk zhihu
请教一下,我没找到 pers/quq/filedb/core/Filter 这个在哪

#89 楼 @victors AutoTraveler/lib/file-handling.jar,jdk 建议用 1.8 的

扫地僧 [该话题已被删除] 中提及了此贴 08月31日 17:56

新手一个,请问 clone 下来用什么 ide? 我用的 eclipse 不行啊?

#92 楼 @oo0vini0oo intelliJ idea

运行会出现以及回到 Home 了还会继续执行的情况(测试环境 Windows)

#94 楼 @adfghzhang 不是回到 Home 就会结束,如果 Home 的节点没遍历完,会继续执行

#95 楼 @quqing 期待你的最新版本。昨天在 windows 上跑了有挺多代码上的问题,捣腾了一番才顺利跑起来。mac 上跑倒是不需要改动。

#90 楼 @quqing 确实 JDK1.8 上是可以奔跑的!赞!
还遇到一个问题,影响性能不知如何破解,UiNode 存储 Id 包含 xpath,之后 pop 出来会根据 xpath 在当前页面进行懒加载,
element 获取不到,抛出 NoSuchElementException,从开始找到抛异常间隔时间太长了,这个时间能否缩短。
Appium Server 端 log 如下

info: [debug] [BOOTSTRAP] [debug] Got data from client: {"cmd":"action","action"
:"find","params":{"strategy":"xpath","selector":"//android.widget.FrameLayout[1]
/android.view.ViewGroup[1]/android.widget.FrameLayout[1]/android.widget.LinearLa
yout[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.wid
get.LinearLayout[2]/android.widget.FrameLayout[1]/android.widget.ListView[1]/and
roid.widget.LinearLayout[1]/android.widget.LinearLayout[2]/android.widget.Linear
Layout[4]","context":"","multiple":false}}
info: [debug] [BOOTSTRAP] [debug] Got command of type ACTION
info: [debug] [BOOTSTRAP] [debug] Got command action: find
info: [debug] [BOOTSTRAP] [debug] Finding //android.widget.FrameLayout[1]/androi
d.view.ViewGroup[1]/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]
/android.widget.FrameLayout[1]/android.widget.LinearLayout[1]/android.widget.Lin
earLayout[2]/android.widget.FrameLayout[1]/android.widget.ListView[1]/android.wi
dget.LinearLayout[1]/android.widget.LinearLayout[2]/android.widget.LinearLayout[
4] using XPATH with the contextId:  multiple: false
info: [debug] [BOOTSTRAP] [debug] Returning result: {"status":7,"value":"Could n
ot find an element using supplied strategy. "}

#97 楼 @victors 有个地方应该能配置的:30,你试试

#98 楼 @quqing 对 webdriver 还是不够熟悉,原来 driver.manage().timeouts().implicitlyWait(second, TimeUnit.SECONDS);瞬间提速许多,😃

#99 楼 @victors 还请帮忙推广下,你们的支持是我更新的动力😃

#100 楼 @quqing 一定的,你将这么好的资源分享出来,非常感谢,赞!
思涵那版截图上做标记,确实不错,控件遍历过程更直观,非常好奇,不知 quqing 是否了解,那版的开源还是需要耐心等待。

#101 楼 @victors 已经做了,新版介绍的帖子里有介绍

#102 楼 @quqing

  1. 截图增加运动轨迹 <!--截图运动轨迹模式 0-错误的时候画 (默认), 1-所有都画--> 1 这个对吧,那就静等 github 源码更新啦。

@quqing 关于 android 的深度遍历,getPageSource 是获取不到页面不可见部分的元素吧,比如 ListView 需要下拉才会显示的部分。看到 github 上的代码 iOS 和 Android 公用一个 dfsSearch,最新的代码有这方面的改进吗?

#104 楼 @Schiffer 还没解决这个问题

#105 楼 @quqing Scala 版的遍历工具有解决这个问题吗?加那个群没加进去...

#106 楼 @Schiffer 不清楚,可能按坐标上下滑动来解决吧,每次移动一段

AutoTraveler.jar 可以在哪里下载

#108 楼 @lpsuper 没上传,有源码可以自己打包

#109 楼 @quqing 源码启动测试是 main.java 吗

#111 楼 @quqing 是 eclipse 建的工程吗 用 intellij 打开好像有问题

学习了 感谢了楼主

谢谢分享。这种思路是很赞的。

@quqing 我的配置:
<?xml version="1.0" encoding="UTF-8"?>

<!--Appium 服务关键字-->

1.0
true
false
zh-Hans


E:/nddoc/soft/commomsoft/activity.apk
1115fb8502812b05
Android
18
1115fb8502812b05
com.nd.sdp.component.debug
com.nd.smartcan.appfactory.demo.SplashActivity
true
true
1200
true

<!--引导流 关键字驱动,引导至所需模块再开始遍历-->

<!--输入类型设置-->
click>>id::com.nd.sdp.component.debug:id/login_account
input>>id::com.nd.sdp.component.debug:id/login_account|421490@ndtest
input>>id::com.nd.sdp.component.debug:id/etPsw|123456
click>>id::com.nd.sdp.component.debug:id/btn_login


<!--窗口鉴定策略,默认取前 8 个节点生成 md5-->
8
<!--Tab 窗口用 selected 区别,可能要多选几个节点到达-->

设置,我,活动>>24

<!--窗口执行顺序 1:从上到下 (默认) 2:从下到上-->
1
<!--控件白名单-->

android.widget.Button
android.widget.ImageButton
android.widget.TextView
android.widget.ImageView
android.widget.FrameLayout
android.widget.LinearLayout


UIATextField
UIASearchBar
UIASecureTextField
android.widget.EditText

<!--启动界面提示框,包含的关键字及操作的控件-->
<!-- 禁止 | 允许>>//android.widget.Button[@resource-id='com.huawei.systemmanager:id/btn_allow']-->
<!--无需返回的列表,需配置识别窗口唯一性的内容-->

设置

<!--黑名单 支持 text,name,resourceId 的过滤-->

相机
退出登录

<!--触发器 条件>>动作 支持返回,点击,手势密码解锁,延时等待-->


<!--过滤级别 1:clazz+text+content_desc+resourceId 2:winID+clazz+text+content_desc+resourceId 3:taskID-->
2
<!--退出遍历的条件:同一窗口内的停留次数-->
10

<!--app 日志-->

adb -s #udid# logcat -b main -b system -b events -b radio *:D | grep xxx


<!--Appium port-->
5757
<!--Appium host-->
127.0.0.1
<!--测试类型 1.android 2.ios 3.web-->
1
<!--运行模式 1.遍历模式 2.业务场景模式-->
1
<!--遍历深度-->
3
<!--截图和视频的目录-->
F:\ndtest\traveler\log
<!--启动 appium-->
D:/Program Files (x86)/Appium/node.exe D:/Program Files (x86)/Appium/node_modules/appium/bin/appium.js -p #port# -bp 5760 --session-override --command-timeout 7200 --udid #udid#
<!--遍历时间 分-->
3
<!--延时等待 秒-->
4
<!--超时 秒-->
30

跑不起来,public void dfsSearch(Stack taskStack, int depth) taskStack.size 一直为 0;麻烦帮我看下 ~

#115 楼 @huangejuan appium 启动命令没有替换掉 port 和 udid,遍历时间只有 3 分钟,建议延长

@quqing 启动,udid 替换了呀,port 也替换成我本机的了

#115 楼 @huangejuan 你是 windows 上跑?我只在 mac 上测试过,windows 上跑可能需要自己修改

@quqing 是的,我在 window 上跑的!
<!--窗口鉴定策略,默认取前 8 个节点生成 md5-->
8
<!--Tab 窗口用 selected 区别,可能要多选几个节点到达-->

设置,我,活动>>24

这个的配置是指页面上 tab 切换吗

@quqing 创建视频报这样的异常
javax.media.NoDataSinkException: Cannot find a DataSink for: com.sun.media.multiplexer.BasicMux$BasicMuxDataSource@2793b2a5
at pers.traveler.review.core.Jim2Mov.saveMovie(Jim2Mov.java:148)
at pers.traveler.review.PicToAvi.convertPicToAvi(PicToAvi.java:64)
at pers.traveler.robot.Robot.afterTravel(Robot.java:255)
at pers.traveler.robot.Robot$1.run(Robot.java:165)
at java.util.TimerThread.mainLoop(Unknown Source)
at java.util.TimerThread.run(Unknown Source)
Caused by: javax.media.NoDataSinkException: Cannot find a DataSink for: com.sun.media.multiplexer.BasicMux$BasicMuxDataSource@2793b2a5
at javax.media.Manager.createDataSink(Manager.java:1894)
at pers.traveler.review.sun.ImagesToMovie.createDataSink(ImagesToMovie.java:177)
at pers.traveler.review.sun.ImagesToMovie.saveMovie(ImagesToMovie.java:115)
at pers.traveler.review.core.Jim2Mov.saveMovie(Jim2Mov.java:144)
... 5 more
是怎么解决呢?

Exception in thread "main" java.lang.NoClassDefFoundError: org/openqa/selenium/C
apabilities
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13)
at pers.traveler.test.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: org.openqa.selenium.Capabilities
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 4 more

報這樣的錯,請問是什麼原因呢?java 是 1.8

#120 楼 @huangejuan 我这边没法重现,从 2 个方面排查
1.配置文件项发给我看看:例如:/Users/mac/Desktop/png
2.装个暴风影音试试,看看是不是编码问题

@quqing F:\ndtest\traveler\log这个是我的配置,正常编码是什么呢 ?
到这一步异常,ImagesToMovie 类中 dsink = createDataSink(p, outML) ,outML 是有值的

#121 楼 @l84222780 用源码跑的还是 jar 包跑的?看错误应该是参数错误导致工厂反射到具体 robot 的时候出的问题
java -jar jar 包名 ios/android 配置文件路径

#123 楼 @huangejuan 改成这样试试F:\\ndtest\\traveler\\log,另外装个暴风影音再试试

#124 楼 @quqing jar 包跑的。java -jar AutoTraveler.jar android config/android1.xml

@quqing 不行,还是报这样的异常,暴风影音也装了

#126 楼 @l84222780 你 windows 跑的还是 mac,如果是 windows 文件分隔符不一样的

#127 楼 @huangejuan 这边也无法重现,有空我跟一下吧

#128 楼 @quqing windows,那 windows 应该是什么形式的呢?

#129 楼 @quqing 换了命令提醒我配置文件是无效的,windows 运行,配置文件还要做修改吗?具体要修改些什么内容呢?

#131 楼 @l84222780 windows 没测试过,工作太忙,过完这阵会更新的,如果急着用可以自己先研究下源代码

quqing,您好,能不能建一个 qq 交流群呀,那样的话交流起来效率会比较高。

#132 楼 @quqing 这两天在 mac 上跑也是这样的报错。请问是为什么呢?打包有问题?addcndeMacBook-Pro:AutoTraveler-master addcn$ java -jar AutoTraveler.jar android confige/android.xml
Exception in thread "main" java.lang.NoClassDefFoundError: org/openqa/selenium/Capabilities
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13)
at pers.traveler.test.Main.main(Main.java:20)
Caused by: java.lang.ClassNotFoundException: org.openqa.selenium.Capabilities
at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 4 more

#134 楼 @l84222780
at pers.traveler.robot.RobotFactory.build(RobotFactory.java:13) 可能是这个问题,你 package 结构有没有改过?

通过反射动态创建的,执行到这一步报错了:Class clazz = Class.forName(Package.ROBOT.replaceAll("#type#", robotType));
public class RobotFactory {
public static Robot build(String robotType, String configFile) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Class clazz = Class.forName(Package.ROBOT.replaceAll("#type#", robotType));
return (Robot) clazz.getConstructor(String.class).newInstance(configFile);
}
}

很有可能是这个 package 找不到:Package.ROBOT
public interface Package {
String DEVICE = "pers.traveler.device.#type#Device";
String ENGINE = "pers.traveler.engine.#type#Engine";
String ROBOT = "pers.traveler.robot.#type#Robot";
}

#135 楼 @quqing 我没动这些,刚去查看也是正常的。可能是我打包有问题吧!~你们是用什么打包的呢?我用的是 Eclipse

你好! 我这边 已经跑起来了! 不过我希望能把每个页面的资源拉出来,检查 里面是否包含有一些特定的值,如:卡壳、系统错误等等异常信息,用来捕获遍历过程中发现业务异常,不知道该怎么做? 或者最新的版本中有考虑这些吗 ?

非常感谢,楼主开源的代码,虽然已经能够做一些改造,但如果作者已经有了这方面的设计,还是希望用原版的,呵呵!

您好,想请教两个问题:1、当前窗口唯一标识符是根据什么原理来确定的? 2、限制遍历深度是为什么呢?一直遍历到底不行,难道是因为怕遍历到底会很深,容易乱?

#140 楼 @chorushe
1、请看文中的章节,“关于窗口唯一性”;
2、遍历深度可以自定义,设置为 0 则不限制

#141 楼 @quqing 如果出现过的页面多了一个图标,恰恰又正好在你取得几个控件内,那么窗口唯一标识符就会变,这时认为他是新窗口吗?

#142 楼 @chorushe 这个文中有写道

8
<!--Tab 窗口用 selected 区别,可能要多选几个节点到达-->

专题,直播,要闻,选股>>24
简单理财,投资,保险,贷款,信用卡>>30

默认取几个节点可以根据实际情况自定义;
对于特殊窗口,找出关键字后可以特殊处理:

#143 楼 @quqing 我的意思是,窗口有细微变化时,不应该认为是新窗口,而您的这个特殊处理还是让他成为新窗口了,我没理解错吧

#144 楼 @chorushe 除非加载图片的是第一个节点,否则通过以上配置可以判断窗口唯一性

@quqing 编译没包含类!
谁有编译好的 jar 包,能发网盘或者我邮箱么 hsqzggg@qq.com,谢谢!

配置好Androidrun以后提示这个
Exception in thread "main" java.lang.IllegalArgumentException: character to be escaped is missing
    at java.util.regex.Matcher.appendReplacement(Matcher.java:809)
    at java.util.regex.Matcher.replaceAll(Matcher.java:955)
    at java.lang.String.replaceAll(String.java:2223)
    at pers.traveler.robot.Robot.beforeTravel(Robot.java:124)
    at pers.traveler.robot.AndroidRobot.working(AndroidRobot.java:57)
    at pers.traveler.robot.Robot.travel(Robot.java:200)
    at pers.traveler.test.Main.main(Main.java:21)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
扫地僧 自动遍历工具 Java 版 (v1.2.0) 中提及了此贴 11月21日 16:26

您好,我的配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>

<!--Appium 服务关键字-->

1.4.16
true
false
zh-Hans


E:/sohuVideoMobile_6.0.2_680_20161123125646.apk
9bb30df4
Android
4.4.2
vivo Xplay3S
com.sohu.sohuvideo
com.sohu.sohuvideo.ui.MainActivity
true
true
true

<!--引导流 关键字驱动,引导至所需模块再开始遍历-->

tips>>禁止 | 允许>>//android.widget.Button[@resource-id='com.huawei.systemmanager:id/btn_allow']
<!--滑动类型设置-->
slide>>2
<!--点击类型设置-->
click>>id::com.xxx.xxx:id/btnnext
click>>id::com.xxx.xxx:id/text_home_head_login
<!--输入类型设置-->
click>>id::com.xxx.xxx:id/otp_tv_login_with_username
input>>id::com.xxx.xxx:id/tv_loginAccount|13012345678
input>>id::com.xxx.xxx:id/loginPassword|123
click>>id::com.xxx.xxx:id/tv_login
<!--手势密码-->
gesture>>xpath:://android.widget.FrameLayout[@resource-id='com.xxx.xxx:id/gesture_container']//android.view.View[1]//android.widget.ImageView
gesture>>xpath:://android.widget.FrameLayout[@resource-id='com.xxx.xxx:id/gesture_container']//android.view.View[1]//android.widget.ImageView


<!--窗口鉴定策略,默认取前 8 个节点生成 md5-->
8
<!--Tab 窗口用 selected 区别,可能要多选几个节点到达-->

专题,直播,要闻,选股>>24
简单理财,投资,保险,贷款,信用卡>>30

<!--窗口执行顺序 1:从上到下 (默认) 2:从下到上-->
1
<!--控件白名单-->

UIAImage
UIAButton
UIASwitch
UIATableCell
UIAStaticText
UIAPickerWheel
UIACollectionCell
android.widget.Button
android.widget.ImageButton
android.widget.TextView
android.widget.ImageView
android.widget.FrameLayout
android.widget.LinearLayout


UIATextField
UIASearchBar
UIASecureTextField
android.widget.EditText

<!--启动界面提示框,包含的关键字及操作的控件-->
禁止 | 允许>>//android.widget.Button[@resource-id='com.huawei.systemmanager:id/btn_allow']
<!--无需返回的列表,需配置识别窗口唯一性的内容-->

首页
财经快讯,我,我的订单,活期,定期

<!--黑名单 支持 text,name,resourceId 的过滤-->

相机
退出登录
如您已完成添加,请重新登录
重新登录
//UIAApplication[1]/UIAWindow[4]/UIAPicker[1]/UIAPickerWheel[1]

//UIAApplication[1]/UIAWindow[1]/UIATableView[1]/UIATableCell[2]/UIACollectionView[1]/UIACollectionCell[5]


<!--触发器 条件>>动作 支持返回,点击,手势密码解锁,延时等待-->

拍照>>back
相册>>back
扫描框>>back
分享到>>back
我的权益>>delay->3
选择银行>>back
选择保险公司>>back
检测到新版本>>back
添加产品 | 保存>>back
消息 | 全部>>back

closeButton>>//android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.ImageButton

安全验证 | 立即验证 | 以后再说>>//android.widget.Button[@resource-id='xxx.xxx.xxx:id/rightBtn']

更多解锁方式>>gesture->//android.widget.FrameLayout[@resource-id='com.xxx.xxx:id/gesture_container']//android.view.View[1]//android.widget.ImageView


<!--过滤级别 1:clazz+text+content_desc+resourceId 2:winID+clazz+text+content_desc+resourceId 3:taskID-->
2
<!--退出遍历的条件:同一窗口内的停留次数-->
10

<!--app 日志-->

adb -s #udid# logcat -b main -b system -b events -b radio *:D | grep xxx


<!--Appium port-->
4723
<!--Appium host-->
127.0.0.1
<!--测试类型 1.android 2.ios 3.web-->
1
<!--运行模式 1.遍历模式 2.业务场景模式-->
1
<!--遍历深度-->
3
<!--截图和视频的目录-->
/Users/mac/Desktop/png
<!--启动 appium-->
/Applications/Appium.app/Contents/Resources/node/bin/node /Applications/Appium.app/Contents/Resources/node_modules/appium/bin/appium.js -p #port# -bp 5760 --session-override --command-timeout 7200 --udid #udid#
<!--遍历时间 分-->
10
<!--延时等待 秒-->
3
<!--超时 秒-->
30

运行时出现如下错误:
2016-11-24 18:33:22 sys_log : [INFO] Appium Server -> http://127.0.0.1:4723/wd/h
ub
log4j:WARN No appenders could be found for logger (org.apache.http.client.protoc
ol.RequestAddCookies).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" org.openqa.selenium.remote.UnreachableBrowserExceptio
n: Could not start a new session. Possible causes are invalid address of the rem
ote server or browser start-up failure.
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58'
System info: host: 'changhe-tj', ip: '10.7.40.59', os.name: 'Windows 7', os.arch
: 'amd64', os.version: '6.1', java.version: '1.8.0_91'
Driver info: driver.version: AndroidDriver
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.ja
va:665)
at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGener
icMobileDriver.java:43)
at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1)
at io.appium.java_client.android.AndroidDriver.execute(AndroidDriver.jav
a:1)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriv
er.java:249)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.jav
a:131)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.jav
a:144)
at io.appium.java_client.DefaultGenericMobileDriver.(DefaultGeneri
cMobileDriver.java:39)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:69)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:78)
at io.appium.java_client.android.AndroidDriver.(AndroidDriver.java
:67)
at pers.traveler.robot.Robot.beforeTravel(Robot.java:118)
at pers.traveler.robot.AndroidRobot.working(AndroidRobot.java:57)
at pers.traveler.robot.Robot.working(Robot.java:163)
at pers.traveler.robot.Robot.travel(Robot.java:202)
at pers.traveler.test.Main.main(Main.java:21)
Caused by: org.openqa.selenium.WebDriverException: org.apache.http.conn.HttpHost
ConnectException: Connect to 127.0.0.1:4723 [/127.0.0.1] failed: Connection refu
sed: connect
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58'
System info: host: 'changhe-tj', ip: '10.7.40.59', os.name: 'Windows 7', os.arch
: 'amd64', os.version: '6.1', java.version: '1.8.0_91'
Driver info: driver.version: AndroidDriver
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumComm
andExecutor.java:76)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.ja
va:644)
... 15 more
Caused by: org.apache.http.conn.HttpHostConnectException: Connect to 127.0.0.1:4
723 [/127.0.0.1] failed: Connection refused: connect
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect
(DefaultHttpClientConnectionOperator.java:151)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(
PoolingHttpClientConnectionManager.java:353)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClie
ntExec.java:380)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.
java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java
:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java
:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttp
Client.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttp
Client.java:71)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttp
Client.java:55)
at org.openqa.selenium.remote.internal.ApacheHttpClient.fallBackExecute(
ApacheHttpClient.java:144)
at org.openqa.selenium.remote.internal.ApacheHttpClient.execute(ApacheHt
tpClient.java:90)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExe
cutor.java:142)
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumComm
andExecutor.java:64)
... 16 more
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocke
t(PlainConnectionSocketFactory.java:74)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect
(DefaultHttpClientConnectionOperator.java:134)
... 29 more
2016-11-24 18:33:32 sys_log : [INFO] 遍历测试持续时间 -> 40.00s
2016-11-24 18:33:42 sys_log : [INFO] 遍历测试持续时间 -> 50.00s
2016-11-24 18:33:52 sys_log : [INFO] 遍历测试持续时间 -> 1.00m
2016-11-24 18:34:02 sys_log : [INFO] 遍历测试持续时间 -> 1.17m
2016-11-24 18:34:12 sys_log : [INFO] 遍历测试持续时间 -> 1.33m

请问这是什么原因呢??

LZ,你的项目是否可以使用 UIautomator2,直接在打包成 apk 跑?

#11 楼 @quqing
a) 截屏功能 Android 没有用 appium 的方法,直接调用命令比基于请求的效率更高; iOS 使用 idevicescreenshot,比 appium 截图提高 10 倍效率。 b) 录像功能 把操作过程转化为流媒体,提升用户体验

idevicescreenshot 适用于真机,iOS 模拟器,有没有效率高的截图工具?
谢谢。

#151 楼 @junewang 这个还真没调研过

#34 楼 @afdsghjk

貌似现在还发不了帖子端午节后把我的脚本也发出来给大家参考用了递归代码行数很少刚学Java的都能看懂

请问这块能发来看一下吗,谢谢!

jennyyung 回复

192.168.56.101:5555,这个是啥?

扫地僧 回复

log4j:ERROR Could not read configuration file [/Users/admin/Desktop/src/log4j.properties].
java.io.FileNotFoundException: /Users/admin/Desktop/src/log4j.properties (No such file or directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.(FileInputStream.java:138)
at java.io.FileInputStream.(FileInputStream.java:93)
at org.apache.log4j.PropertyConfigurator.doConfigure(PropertyConfigurator.java:306)
at org.apache.log4j.PropertyConfigurator.configure(PropertyConfigurator.java:324)
at pers.traveler.log.Log.(Log.java:17)
at pers.traveler.robot.Robot.travel(Robot.java:172)
at pers.traveler.test.Main.main(Main.java:21)
log4j:ERROR Ignoring configuration file [/Users/admin/Desktop/src/log4j.properties].

该怎么搞啊?

楼主在使用 Windows 引导执行 driver.manage().window().getSize().width;就报了下面这个错误,我把部分功能提取出来单独运行没有任何问题,不知道有没有人遇到类似的情况,折腾 1 天了 还是没解决
error: Unhandled error: Error: read ECONNRESET
at exports._errnoException (util.js:896:11)
at TCP.onread (net.js:556:26) context: [POST /wd/hub/session {"desiredCapab
lities":{"app":"D:\AndroidSDK\sdk\build-tools\22.0.1\doctor.apk","appPacka
e":"com.szyino.patientclient","noReset":"false","noSign":"true","platformVersio
":"4.4.4","unicodeKeyboar]

李海默 回复

在 WINDOWS 运行了 Appium 服务后就不能再使用 adb 不然 session 会话会丢失。

@quqing 你好,问下节点的唯一性是如何解决的呢,可能会变化的吧

160楼 已删除

请问下,报以下错误是撒回事了
@quqing
Exception in thread "main" org.openqa.selenium.WebDriverException: An unknown server-side error occurred while processing the command. Original error: Failed to set language: zh-CN and country: null (WARNING: The server did not provide any stacktrace information)
Command duration or timeout: 2.39 seconds
Build info: version: '2.53.0', revision: '35ae25b', time: '2016-03-15 17:00:58'
System info: host: 'MacBook-Pro.local', ip: '10.88.xx.xx', os.name: 'Mac OS X', os.arch: 'x86_64', os.version: '10.14.5', java.version: '1.8.0_211'
Driver info: io.appium.java_client.android.AndroidDriver
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.openqa.selenium.remote.ErrorHandler.createThrowable(ErrorHandler.java:206)
at org.openqa.selenium.remote.ErrorHandler.throwIfResponseFailed(ErrorHandler.java:158)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:678)
at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:43)
at io.appium.java_client.AppiumDriver.execute(AppiumDriver.java:1)
at io.appium.java_client.android.AndroidDriver.execute(AndroidDriver.java:1)
at org.openqa.selenium.remote.RemoteWebDriver.startSession(RemoteWebDriver.java:249)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:131)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:144)
at io.appium.java_client.DefaultGenericMobileDriver.(DefaultGenericMobileDriver.java:39)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:69)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:78)
at io.appium.java_client.android.AndroidDriver.(AndroidDriver.java:67)
at pers.traveler.robot.Robot.beforeTravel(Robot.java:118)
at pers.traveler.robot.AndroidRobot.working(AndroidRobot.java:57)
at pers.traveler.robot.Robot.travel(Robot.java:200)
at pers.traveler.test.TravelerTest.main(TravelerTest.java:18)

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册