在 TesterHome 挺久了,一般是看帖比较少发,
最近有看到一些同学做多设备的 Appium 测试有碰到一些问题。
刚好自己前段也涉及到类似的方案,在这里分享些自己这段碰到的一些问题阻碍,和解决的一些思路吧,刚涉及 UI 自动化不久,很多东西还很生疏,欢迎大家提点。

1.需求和条件制约
最早需求的提出,场景的要求是要同时实现几十台设备的 UI 自动化测试且是有客户端的交互行为,部分设备运行的 APP 需要等待其他设备的 APP 进行操作。
APP 是 Hybrid 类型的,内嵌了 webview,且该 webview 是自定义过的,robutim 是无法识别到控件,尝试过录制器也无法用,而尝试了 appium 则是可以使用的因为是 chromedriver 控制的。
设备数量大概是在 60 左右,要求持续运行较久的时间,那么设备需要持续 USB 供电,就不能直接连 PC 跑脚本 (PS:PC 也没那么多口接这么多设备)
那么需求就清晰了:用 appium 在同个用例里进行 60 台设备的 UI 自动化控制。

2.方案实施

  1. 首先因为无法 USB 连接,这里全部都使用无线 ADB 连接,之前有同学写过使用无线 adb 工具进行连接的文章,其实只要有 root 权限,一个命令就可以搞定了。 在 cmd 运行 adb tcpip 5555,可以启动 5555 端口的监听,如果需要切换回 usb 则运行 adb usb 打开tcpip监听 之后通过 adb connect IP 进行设备的无线调试连接,adb disconnect IP 来断开连接 这里写图片描述

2.然后开始多设备时的 Appium Server 控制,因为 Appium Server 与设备要一一对应,所以需要启动较多的 server,所以肯定不选择 windows 版本的 server 控制,因为启动需要手动去点击,出现问题也无法重新启动。
那么就使用命令行模式的 (PS:部分同学因为被墙的原因无法 npm 进行安装,这里有个小技巧,可以帮助你们装完 windows 版的 appium server 后依然可以使用命令行的:传送门)

设计 AppiumServer 类来进行 Server 的批量启动控制如下。

public class AppiumServer {
    LogUtil log = new LogUtil();

    public AppiumServer(){      
        KillTask("node.exe");
        log.info("init appium server...");
    }

    public void KillTask(String taskname){
        String Command = "taskkill /F /im " + taskname;
        log.info("kill " + taskname + " task ...");
        runCommand(Command);
    }

    public void runServer(int port,String udid) {
        log.info("run " + udid + " Appium Server in port " + port + "...");
        int bpport = port +1;
        int chromeport = port + 4792;
        //多设备server端需要手动指定每台设备的udid,安卓无线连接下就是设备的ip:port..
        String Command = "appium.cmd -p " + port + " -bp " + bpport + " --session-override --chromedriver-port "+ chromeport +" -U " 
                         + udid +  " >c://" + port + ".txt";
        log.info(Command);
        runCommand(Command);
    }

    private void runCommand(String command){
        try {
            Runtime.getRuntime().exec(command);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

启动 server

AppiumServer AS = new AppiumServer();
AS.runServer(4723, "192.168.1.17:5555");

3.对于同时启动大量的 AppiumServer 运行 webview 的测试,在实际运行时,出现问题了,当启动的设备超过十几台时候,adb 的进程数会达到几十个,数量达到一定程度后,adb 会停止响应并结束进程断开所有设备,而且 webview 进行测试的时候会需要启动 chromedriver 进程,与设备也是一一对应,启动超过十几个进程并行执行,也会出现很多不稳定的不识别或者停止响应的情况。
针对这个问题,其实原因还是 windows 下的 adb 太脆弱了,结合 Appium 的 Server 支持远程执行的情况,搭建多台 Linux 环境的 PC 来做 Appium Server 端的运行执行 (最后实际测试 linux 下同时可以承载 30+ 台设备妥妥的),Server 的启动控制使用 STAF 进行控制。(PS:STAF 环境搭建之后再整理份,不过百度下应该挺多)
搭建好 STAF 环境后,导入 JSTAF.jar 并增加 staf 的运行控制方法如下

import com.ibm.staf.STAFException;
import com.ibm.staf.STAFHandle;

    ......
    private void runRemoteCommand(String command){
         try {
             handle = new STAFHandle("MySTAF");
         } catch (STAFException e) {
             System.out.println("Error registering with STAF, RC: " + e.rc);
         }
         String service = "PROCESS";
         String request = "start command shell \"" + command + "\"";
         try {
            handle.submit2(ServerIP, service, request);
            handle.unRegister();
         } catch (STAFException e) {
             System.out.println("run process error");
         }       
    }

修改 LinuxServer 的启动方法如下:

private String LinuxAppiumPath = "/usr/local/lib/node_modules/appium/bin";
public AppiumServer(String ServerType, String ip){
        this.setServerType(ServerType);
        this.setServerIP(ip);
        this.isRemote = true;
        KillTask("node");
        log.info("init remote appium server at "+ ip +"...");
    }
......
public void runLinuxServer(int port,String udid) {
        log.info("run " + udid + " Appium Server in port " + port + "...");
        int bpport = port +1;
        int chromeport = port + 4792;
        String Command = "/usr/bin/node " + LinuxAppiumPath +"/appium.js -a " + ServerIP +" -p " + port + " -bp " + bpport + " --session-override --chromedriver-port "+ chromeport +" -U " + udid +  " >/home/wang/log/" + port + ".log";
        log.info(Command);
        runRemoteCommand(Command);      
    }

(当然 AppiumServer 类还需要进行一些对应的逻辑调整,比如区分开远程和本地的 Server 执行的方法,区分 Server 的 Type,区分 IP 等。这里就不细说了)
3.流程图
大致画了个简单原理的流程图如下:
这里写图片描述

4.运行结果
具体的用例构建和脚本是用 testNG 加 testReport 来处理的,这里也不细写了,运行的部分日志结果如下:
这里写图片描述


↙↙↙阅读原文可查看相关链接,并与作者交流