iOS 测试 NewLLDebugTool——ios monkey 保活

haleli · July 13, 2019 · Last by haleli replied at July 14, 2019 · 1911 hits

NewLLDebugTool里面实现了一个智能 monkey,现在已经支持快速遍历算法和随机遍历算法,因为 monkey 是集成在 app 里面,如果 app 发生 crash(如果开启 fuzzy 功能,很容易发生 crash),那么如何重新拉起 app,继续运行呢?

app 自己本身肯定无法拉起自己,肯定需要外力进行拉起,我自己调研有两种可行的方案:
1、通过私有 api 进行拉起,使用的私有 api LSApplicationWorkspace,大体代码如下所示:

+(BOOL)isOpenApp:(NSString*)appIdentifierName
{
    Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
    NSObject* workspace = [LSApplicationWorkspace_class performSelector:@selector(defaultWorkspace)];
    BOOL isOpenApp = [workspace performSelector:@selector(openApplicationWithBundleID:) withObject:appIdentifierName];

    return isOpenApp;
}

这种方案最终我放弃了,主要考虑以下几点:1、私有 api,顾名思义是苹果不允许使用的 api,很可能在苹果的后续版本更新中就无法使用了,从而导致这种保活方式的可兼容性很差; 2、可扩展能力不强,如果后续我们想接入持续集成 (未来的趋势),那必定需要定时或者 ci 的时候拉去最新的安装包跑 monkey,很明显自动拉取最新包并安装到手机上,是通过私有 api 完成不了的,如果能完成,ios 全家桶了解一下。

2、方案二也是现在我采用的方式,通过 ios 自带的 ui 自动化框架 xctest 进行保活。xctest 里面的XCUIApplication可以检查当前 app 的状态是否激活,如果未激活可以拉起 app,大体代码如下所示:

public func addXCTestCheckCurrentApp(interval:Int, application:XCUIApplication) {
       addCheck(interval:interval){ [weak self] in
           let work = DispatchWorkItem(qos:.userInteractive){
               if (application.state != XCUIApplication.State.runningForeground){
                   application.activate()
                   self?.sleep(5)
               }
           }
           DispatchQueue.main.async(execute:work)
       }
   }

工程也已经开源,开源地址:MonkeyRunner,和 monkey 配合使用,效果更好。效果如下所示:

中间触发了一次预先写好的 crash,app 闪退,但是被 xctest 重新拉起,monkey 继续运行,如果我们想要停止 monkey,只需要摇一摇手机停止 monkey 即可。

最佳回复

monkey 集成到 app 里面,另外的 monkeyrunner 用 iOS 自带的 UITESTS 作为执行器运行 monkey,起初为啥不直接在 UITEST 里写 monkey 相关的逻辑呢

haleli 回复

1、虽然弹层页面会获取到遮挡的页面元素,我们应该想办法过滤掉不可视的元素,这里也是有办法用微小的耗时方式过滤掉不可视的 UI 元素的,最后还是对于元素的精准识别探讨吧
2、XCUITEST 是可以拿到屏幕外的 cell 或者其他控件类型的元素,视频中有对屏幕之外的元素进行测试的演示,无论元素在横向或是纵向,都是可以计算的方式自动跟踪定位元素的
3、pickview 等自定义视图的非触点的控件来看,也是通过配置化的方式来做,还没想到更好的方式
4、cocos 还是不熟悉,这里面就不探讨了,后续用我原先的工具试跑下看效果,熟悉下
5、忙过这阵子也会开源出来的

共收到 6 条回复 时间 点赞

monkey 集成到 app 里面,另外的 monkeyrunner 用 iOS 自带的 UITESTS 作为执行器运行 monkey,起初为啥不直接在 UITEST 里写 monkey 相关的逻辑呢

haleli #2 · July 14, 2019 Author

你这个问题挺好的,我们也在内部讨论了好多次,现在回答一下:

基于 xctest 版的智能 monkey,我们也有开发过,但是问题也很明显,简单列举几条,比如说无法做到精确的控件识别,例如被遮挡或者无意义的控件也会被识别出来,无法细腻的控件操作,例如对于 pickview 会被识别成 otherelment,对于 tableviewcell 只能查找出屏幕内的。

如果基于 KIF 实现智能 monkey,是可以将我现在的这套执行 ui 的逻辑的那一部分提取出来的,但是侵入代码是必不可少的,比如控件树的获取,比如要 hook 代码里面的 accessbility(KIF 是基于 accessbility 操作 ui 的)、还有外围的操作,比如 js error 的捕获、提单、性能的上报等操作 (只实现一个 monkey 是没有啥作用的,monkey 只是一种 ui 执行的手段),比如集成到 sdk 里面的 http/https mock 实现 fuzzy 测试。

还有一个最重要的原因就是我本身负责的项目是基于 cocos 开发 (一个游戏引擎) 的,我要实现 cocos monkey 在 xctest 是无法实现的,必须要在代码里面实现 monkey,这个我就不仔细展开了。

写了这么多,啰哩啰嗦的
总结:
基于 xctest 实现智能 monkey,我这边出现的问题很多,坑也很多,不建议
基于 kif 实现智能 monkey,你如果实现了的话,记得回复一下我这个帖子,我也学习一下😁 ,多交流

值得交流下,看了你写的侵入性的 Monkey,无法做到精确的控件识别,应该是 Monkey 对于元素的精准识别还有提升的空间吧
1、用 XCUITEST 最重要是为了省事,一套代码兼容 Native、WebView、RN 等跨平台页面,对外无侵入性接入
2、Crash 捕获、网络数据 (proxy 或 mock)、性能监控、统计覆盖率,交给集成到客户端的 SDK 做些可测性改进,日志相关的回捞 SDK 的日志显示在报表上,场景构造相关的外部修改下数据。还有项目用 cocos 开发,也就是游戏相关的 Monkey 测试,归类为跨平台页面,一般是集成到 iOS 客户端上,我都会想办法对跨平台的页面做些可测性改进,这里需要具体分析,例如 RN 页面多个小 UI 组件会嵌套到一个大 UI 组件里,无法识别小 UI 组件就需要让研发在底层的 RN 框架里加上 Accessibility 辅助功能标签;Crash 捕获、性能监控、日志相关的只要做的到位也是可以用黑盒的方式做部分实现的
3、获取控件树,无论是侵入性 XCTEST 方式或是非侵入性 XCUITEST 的 Accessibility 辅助功能都是用来提取页面控件树,最显著的区别在于使用 XCUITEST 在 loadMore 功能的页面获取 source 控件树耗时相当长,还容易出现非正常的意外崩溃,不过最后这些问题也都解决了,提取每个页面的控件树只需要 0.3 秒左右,极个别页面最慢也不会超过 10 秒
4、最后还是建议你抽出代码,提取部分插桩能够用指令下发 UI 操作即可,离智能也会更近一些吧
5、我也有一段跟你相似写 Monkey 的经历,试过用 XCUITEST 无侵入性写的效果,录了一段 App 视频效果验收: https://pan.baidu.com/s/1wYc_ZETUICzSDa71fHzRrA#list/path=%2F

haleli #4 · July 14, 2019 Author

第二点不讨论,Crash 捕获、网络数据 (proxy 或 mock)、性能监控、统计覆盖率,交给集成到客户端的 SDK 做些可测性改进。这个可以使用其他的 sdk,因为我也是集成的开源的相关的 sdk,后续有时间会以插件的方式进行改进。

第一点:获取控件树,举个例子,比如说界面有一个弹窗,遮挡了下面的界面,就不应该获取被遮挡的界面,xctest 获取的控件树是可以将遮挡和被遮挡的界面都获取到的,这个就会导致如果点击被遮挡的控件,其实点击的是它的上层控件,想点击的和被点击的不是同一个控件,导致整个算法记录的跳转关系错乱。

比如说有一个 tableview,里面有很多的 tableviewcell 是在屏幕外,xctest 是无法获取到屏幕外 cell 的控件,这个就会导致 xctest 是无法点击到屏幕外的 cell,就不会遍历整个 app 的控件。

第二点:cocos monkey,cocos 是游戏引擎,和 rnn 不一样,是使用 js 写的,如果想要获取 js 的控件,需要注入我们的一个写好的脚本,才可以拿到 cocos 的控件。具体代码可以参考这个脚本:https://github.com/didiaodanding/NewLLDebugTool/blob/master/NewLLDebugTool/js/monkey_driver.js

第三点:对于控件的操作,比如说对于一个 pickview 的组件,在 xctest 里拿到的是 otherelement,就没有办法对 pickview 做选取的操作,可能只会对其发送一个点击操作,实际上 xctest 里面拿到的大多数都是 otherelement,也只能对其发送一个点击操作。

基于 xctest 的智能 monkey,我们也有开发,如果可以的话,可以把你的代码给我开个权限吗,我读一读就应该了解了

haleli 回复

1、虽然弹层页面会获取到遮挡的页面元素,我们应该想办法过滤掉不可视的元素,这里也是有办法用微小的耗时方式过滤掉不可视的 UI 元素的,最后还是对于元素的精准识别探讨吧
2、XCUITEST 是可以拿到屏幕外的 cell 或者其他控件类型的元素,视频中有对屏幕之外的元素进行测试的演示,无论元素在横向或是纵向,都是可以计算的方式自动跟踪定位元素的
3、pickview 等自定义视图的非触点的控件来看,也是通过配置化的方式来做,还没想到更好的方式
4、cocos 还是不熟悉,这里面就不探讨了,后续用我原先的工具试跑下看效果,熟悉下
5、忙过这阵子也会开源出来的

haleli #6 · July 14, 2019 Author
ltyd5788 回复

嗯嗯,如果可以把我提出的问题解决掉,是一个很好的解决方案,期待开源

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up