Appium踩坑之旅——小米手机

踩坑之旅

Posted by Mio4kon on 2017-06-12

前言

了解过 Appium 或看过我之前写的系列教程的人,一定知道在与Appium Server连接的时候会传入相应的capabilities.

  • platformName
  • automationName
  • autoLaunch
  • noSign
  • ….

automationName是告诉 Appium Server是用什么方式来操作UI元素的.对应源码来说就是使用什么样的 Driver.(下一篇教程会简单的过一遍 Appium Server的源码)

由于uiautomator2的执行效率以及支持Toast验证.使得在写测试用例的时候应优先使用uiautomator2.

不过当我使用uiautomator2后,尤其是小米手机,那可是一坑接一坑..

坑一:autoLaunch

由于公司的平台希望能在跑Case前由平台来控制打开App.所以我将autoLaunch设置为false.起初在我的乐视手机上没有任何问题.但是当换到一台未跑过测试代码的小米手机时候,发现一只报一个奇葩的错误:

1
2
3
4
5
6
7
[MJSONWP] Encountered internal error running command: ProxyRequestError: Could not proxy command to remote server. Original error: Error: socket hang up
at JWProxy.proxy$ (../../../lib/jsonwp-proxy/proxy.js:153:13)
at tryCatch (/Users/mio4kon/code/mio4kon_github/appium/node_modules/babel-runtime/regenerator/runtime.js:67:40)
at GeneratorFunctionPrototype.invoke [as _invoke] (/Users/mio4kon/code/mio4kon_github/appium/node_modules/babel-runtime/regenerator/runtime.js:315:22)
at GeneratorFunctionPrototype.prototype.(anonymous function) [as throw] (/Users/mio4kon/code/mio4kon_github/appium/node_modules/babel-runtime/regenerator/runtime.js:100:21)
at GeneratorFunctionPrototype.invoke (/Users/mio4kon/code/mio4kon_github/appium/node_modules/babel-runtime/regenerator/runtime.js:136:37)
[HTTP] <-- POST /wd/hub/session 500 31412 ms - 216

好吧.当时也不知道为啥我就试着把uiautomator2这条删掉.发现一切OK了.竟然能跑起来了.根据多年的控制变量法我得出了”看来小米手机可能不支持uiautomator2吧”. native

于是后来我就为了兼容小米手机对case做了适配.但是不能验证Toast这个问题就变得很棘手了.所以还要避免用Toast来验证case的成功还是失败.再后来我发现小米手机在使用uiautomator1的时候.会存在反馈特别慢甚至会超时或者找不到等等的问题.在Appium上提ISSUE得到的反馈是使用uiautomator2再试试看.Orz…

还是得动手靠自己.就此我打算研究下 Appium Server的源码,想要解决的问题有下面两点:

  1. 为什么小米手机在用 uiautomator1 的时候会存在服务端返回超时或找不到元素的情况(必现)
  2. 为什么小米手机Api>18却不支持 uiautomator2呢?是不是我使用姿势的问题?

带着原因我开始翻源码了(这篇文章不过多涉及源码.主要说结论)

在说这两个问题之前先说明一个问题:之所以AppiumServer可以控制手机的UI,是因为Appium会在手机上开一个Server,通过AppiumServerPhoneServer之间的通讯来控制手机UI.

第一个问题.Debug发现AppiumServerPhoneServer之间的通讯没有问题.问题主要在于调用uiautomator1的api时抛了异常.但是这个PhoneServer的工程编译各种问题.所以第一个问题暂时先放下.暂时归结于uiautomator1不稳定.有兴趣的小伙伴可以看看这个PhoneServer的源码.

https://github.com/appium/appium-android-bootstrap

于是我开始把主要精力放在第二个问题上.因为如果解决第二个问题.那么Toast验证的问题就迎刃而解了.而且现在主流的手机都在Api18以上,可以放心使用uiautomator2.

直接上源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[appium-uiautomator2-driver:driver.js]
// If the user sets autoLaunch to false, they are responsible for initAUT() and startAUT()
if (this.opts.autoLaunch) {
// set up app under test
// prepare our actual AUT, get it on the device, etc...
await this.initAUT();
}
async initAUT () {
....
await this.grantPermissions();
await this.uiautomator2.installServerApk();
}

可以发现只要autoLaunch为false,就不会安装ServerApk…

问题终于找到了.我自己装还不行嘛!! 好了有遇到下一个坑.

坑二:神隐模式

当我安装好了ServerApk,再跑用例的时候发现另一个神奇的问题.


好吧.又报了个这个错.不过这次过程明显感到不一样.这次可以明细感觉比之前要慢很多.这是因为实际上uiautomator2的服务还在运行.但是始终连不上.

为什么连不上呢?由于这个是小米手机.而小米有一个神隐模式.简单来说就是后台的应用禁止网络.而uiautomator2的服务恰好是通过代理转发返回响应消息的.这就使得服务一直没有响应,最终报错.

所有一定要把uiautomator2Server加入神隐模式的白名单,或者关闭神隐模式.

坑三:INJECT_EVENTS

解决了上面两个坑.你会发现可以顺利的打开App进行测试了.你以为这就完了?
跑完用例你会发现所有的case全部失败了.什么鬼?我用uiautomator1跑用例好好的.怎么一换成uiautomator2全是失败?

case最后会发现错误信息:

1
2
3
4
selenium.common.exceptions.WebDriverException: Message: An unknown server-side
error occurred while processing the command. Original error:
java.lang.SecurityException: Injecting to another application requires
INJECT_EVENTS permission

INJECT_EVENTS permission又是什么鬼.

网上搜了一下才发现小米手机需要打开USB调试(安全设置)才可以.但是之前没打开这个设置uiautomator1好像也并没有什么问题.

总之在开发者模式中打开这个选项.你就可以放心的在小米手机上使用uiautomator2了.

这三个坑虽然解决起来相当简单.但是当你第一次遇到的时候真的是一脸懵逼.尤其是前面两个坑.网上都找不到原因的.只能靠半猜半试来寻找原因.

下一篇将会对 appium server 的源码进行分析.梳理 server的流程,有助于解决一些莫名其妙的问题.