Appetizer 字节码插桩监控 + 自动遍历的 Android 自动化测试实战 —— 开源 Bilibili 客户端

AppetizerIO · 2017年04月01日 · 最后由 Elsie 回复于 2019年04月26日 · 4580 次阅读
本帖已被设为精华帖!

背景

Android 应用的自动遍历,能够全自动的走遍整个被测试应用的所有层级页面,在一定程度上做到取代手动点击测试。但是在自动遍历的过程中,收集到的数据十分有限。

如果结合自动遍历与监控,同时多台设备多次遍历,就能够收集到海量的数据,包括 App 图片解码速度,各个页面的流量开销,CPU 和堆内存占用等,对这些信息进行过滤,可以得到有价值的性能分析报告。

本文对开源 bilibili 客户端进行测试,该客户端使用了 retrofit,Rx 系列,butterknife 等热门三方库,能够正常播放 Bilibili 主站视频,还包括一些功能性页面,是一个和商用 App 接近的开源项目。

工具选择

字节码插桩监控工具使用了 Appetizer 质量监控,使用 Appetizer 上传 bilibili 客户端的 APK 文件,在服务端进行字节码插桩后,下载到本地,在手机上安装。

自动遍历使用了 AppCrawler,配置文件中对 bilibili 的登录页面进行简单的配置后,便可以自动化的遍历整个 App。

Appetizer 插桩质量监控基本流程如下图:

在自动测试流程中使用 AppCrawler,在自动化遍历的同时,扩展出了自动截图功能,结合底层数据和 UI 截图,能获取更多信息。

实战步骤

步骤一:Appetizer 质量监控插桩

参考该教程。打开 Appetizer 客户端,登录后选择 “质量监控”,点击 “插桩 APK 文件”,等待被插桩的 APK 文件下载到本地。

或者使用命令行版的 Appetizer 质量监控客户端,依次执行 login, process 操作得到被插桩的 APK 文件。

步骤二:AppCrawler 自动遍历

根据 AppCrawler 的教程,安装好相关的依赖。由于 bilibili 客户端的第一个页面是登录页面,无法通过自动遍历跳过,需要单独配置,剩余页面都可以自动遍历。

triggerActions:
- action: "azard"
  xpath: "//*[@contains(name(), "请输入您的哔哩哔哩账号")]"
  times: 0
- action: "xxx"
  xpath: "//*[@contains(name(), "请输入您的密码")]"
  times: 0
- action: "click"
  xpath: "//*[@contains(name(), "登录")]"
  times: 0

=============一台古老 Nexus5 自动遍历 20 分钟的分割线=============
=============一台古老 Nexus5 自动遍历 20 分钟的分割线=============
=============一台古老 Nexus5 自动遍历 20 分钟的分割线=============

报表一:AppCrawler 自动遍历报表

AppCrawler 在自动遍历结束后会得到一个文件夹,有一个网页可视化的报表。从报表上可以看到一台设备运行了 108 个 Activity 页面,实测大概 20 分钟左右,H5 页面也会进行一定程度的遍历但报表不会抓取具体信息。

同时,AppCrawler 在每次点击操作前后都会进行截图,方便查看具体渲染的行为状态。

报表二:Appetizer 质量监控插桩报表

在自动化遍历结束后,通过 Appetizer 客户端或 Appetizer 质量监控命令行客户端,将设备上的质量监控 log 上传服务端进行深度分析,得到 json 格式的报表,报表可以使用 Appetizer 客户端打开进行可视化。

可以看到 Appetizer 质量监控抓取到了若干问题,包括数个高延迟,可缓存的建议,一个 HTTP 错误,若干耗时操作和性能问题。

点击HTTP 错误,可以看到具体的 HTTP 错误内容,以及调用该 HTTP 请求时的函数堆栈。

在笔者实战测试的过程中,抓取到一个控指针错误并导致 bilibili 客户端崩溃,Appetizer 质量监控抓取到了该错误,并且包含函数堆栈和当前时刻的内存占用。

点击分享按钮可以自动将问题生成 markdown 格式的 issue,发布到 GitHub 上。

该 issue 得到了开发者的确认,是由于 HTTP 调用可能返回空数据,导致渲染出错造成的。

总结

通过结合 Appetizer 质量监控插桩,和 AppCrawler 自动化遍历,可以大幅度减少手动测试的工作量,并且抓取到需要底层数据,实现对 App 更深度的测试分析。

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

只让免费使用一段时间 不友好 哈哈

大神们,菜鸟请教一个问题,就是执行 appcrawler 时出现上面的报错信息,这是怎么回事呢

Driver info: driver.version: AndroidDriver
at io.appium.java_client.remote.AppiumCommandExecutor.lambda$2(AppiumCommandExecutor.java:101)
at java.util.Optional.orElseGet(Unknown Source)
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:100)
at org.openqa.selenium.remote.RemoteWebDriver.execute(RemoteWebDriver.java:586)
at io.appium.java_client.DefaultGenericMobileDriver.execute(DefaultGenericMobileDriver.java:42)
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:217)
at org.openqa.selenium.remote.RemoteWebDriver.(RemoteWebDriver.java:140)
at io.appium.java_client.DefaultGenericMobileDriver.(DefaultGenericMobileDriver.java:38)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:83)
at io.appium.java_client.AppiumDriver.(AppiumDriver.java:93)
at io.appium.java_client.android.AndroidDriver.(AndroidDriver.java:72)
at com.testerhome.appcrawler.driver.AppiumClient.appium(AppiumClient.scala:163)
at com.testerhome.appcrawler.driver.AppiumClient.(AppiumClient.scala:39)
at com.testerhome.appcrawler.Crawler.setupAppium(Crawler.scala:263)
at com.testerhome.appcrawler.Crawler.start(Crawler.scala:133)
at com.testerhome.appcrawler.AppCrawler$.startCrawl(AppCrawler.scala:344)
at com.testerhome.appcrawler.AppCrawler$.parseParams(AppCrawler.scala:312)
at com.testerhome.appcrawler.AppCrawler$.main(AppCrawler.scala:92)
at com.testerhome.appcrawler.AppCrawler.main(AppCrawler.scala)
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.connectSocket(PlainConnectionSocketFactory.java:75)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:359)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:381)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:72)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at org.openqa.selenium.remote.internal.ApacheHttpClient.fallBackExecute(ApacheHttpClient.java:138)
at org.openqa.selenium.remote.internal.ApacheHttpClient.execute(ApacheHttpClient.java:86)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:337)
at org.openqa.selenium.remote.ProtocolHandshake.createSession(ProtocolHandshake.java:136)
at org.openqa.selenium.remote.HttpCommandExecutor.execute(HttpCommandExecutor.java:142)
at io.appium.java_client.remote.AppiumCommandExecutor.execute(AppiumCommandExecutor.java:89)
... 18 more

感谢分享

mark,感谢分享

赵欢 回复

加群来讨论下: 467889502

好像不能捕获 post 请求

老哥稳

这个 AppCrawler 的功能, 可以参考这里https://testerhome.com/topics/node83

triggerActions:

  • action: "azard" xpath: "//*[@contains(name(), "请输入您的哔哩哔哩账号")]" times: 0
  • action: "xxx" xpath: "//*[@contains(name(), "请输入您的密码")]" times: 0
  • action: "click" xpath: "//*[@contains(name(), "登录")]" times: 0

你这个是写到 congfig 文件里么 还是怎么操作 能够让他登录

请更新个人资料中的微信和支付宝的打赏二维码,用于精华帖打赏

赞,Appetizer 和 AppCrawler 的完美使用结合

AppetizerIO Appetizer 基于字节码插桩的质量监控 中提及了此贴 04月01日 19:06

不错不错

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

大部分功能都可以一键完成,用户体验很赞呀~

实践是检验真理的唯一标准,赞

赞,必须赞。

恒温 将本帖设为了精华贴 04月01日 13:16

这个很棒

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