• 06 Sonic 实现多设备运行


    我们做的是音视频的app,和一般app不同需要一个对手。例如,一台设备推流后,需要另一台设备拉流,这样一般自动化平台和sonic都不能满足需求。所以才想基于sonic的基础上,稍加修改来实现这个需求。

    一、现状分析

    sonic现有功能。

    1、在测试用例管理中,创建两个用例。case1 打开app并点击一个按钮。case2点击返回按钮并点击一个按钮。

     2、创建两个套件,test 套件调用case,test2 套件调用case2。

    3、先运行 test 再运行 test2。这样就初步实现一个case用不同设备运行的效果。但有两个问题

    • 每次运行套件都需要 Appium server ,每次启动都需要10S左右,有点慢
    • 不知道 test 套件1啥时候运行完成,也就是不知道啥时候运行 test2 套件
    • 同一个case的运行结果显示到了两个结果里
    • 同一个case拆的有点乱,脚本看着有些费劲。脚本要是多了,看着更乱。

    二、处理路线图

    1、只改 web + server 端。修改 runSuite 接口调用完case1再屌用case2,实现一次调用运行两个case。需要创建一个线程判断case1运行完成后再调用case2

    2、配合修改 Agent ,创建  Appium server 后保持,直到收到断开命令后再释放 Appium server。修改上报运行结果,将多个case的结果报到一起

    以上两步实现后,基本就可以满足需求了。

    3、重写sonic用例编辑,实现添加步骤的同时指定设备类型。

    4、重写测试套件运行,运行时重新整理case实现多设备,多设备类型运行case。

    三、只改 web + server 端。修改 runSuite 接口调用完case1再调用case2

    TestSuitesServiceImpl 的 218 行 NettyServer.getMap().get(id).writeAndFlush(result.toJSONString()); 将构造的数据发给的agent,所以可以在这里处理

    3.1、同步 发送 test 套件,休眠20S后再发送 test2 套件

    1、web上运行 test 套件

     2、调试模式在 TestSuitesServiceImpl.java 的 172 行打断点,这里就是在数据库中生成一个测试结果记录。

    看到改动的表是 results

    3、在 180 行打断点,记录的是发个 agent 的运行脚本,如下:

    test套件发的内容如下:

    {"msg":"suite","cases":[{"gp":{},"rid":14,"steps":[{"step":{"caseId":1,"conditionType":0,"content":"","elements":[],"error":3,"id":1,"parentId":0,"platform":1,"projectId":1,"sort":1,"stepType":"openApp","text":"com.example.tablayouttest"}},{"step":{"caseId":1,"conditionType":0,"content":"","elements":[{"eleName":"点击 quickstart","eleType":"xpath","eleValue":"//android.widget.Button[@text='QUICKSTART']","id":1,"projectId":1}],"error":3,"id":2,"parentId":0,"platform":1,"projectId":1,"sort":2,"stepType":"click","text":""}}],"device":[{"agentId":1,"chiName":"","cpu":"arm64-v8a","gear":0,"id":1,"imgUrl":"","manufacturer":"Meizu","model":"M1822","name":"meizu_M1822_CN","nickName":"","password":"","platform":1,"position":0,"size":"1080x2160","status":"ONLINE","udId":"822QEDU5227H3","user":"lrs","version":"8.1.0"}],"cid":1}],"pf":1}
    

    test2套件发的内容如下:

    {"msg":"suite","cases":[{"gp":{},"rid":20,"steps":[{"step":{"caseId":2,"conditionType":0,"content":"BACK","elements":[],"error":3,"id":3,"parentId":0,"platform":1,"projectId":1,"sort":3,"stepType":"keyCode","text":""}},{"step":{"caseId":2,"conditionType":0,"content":"","elements":[{"eleName":"点击 quickstart","eleType":"xpath","eleValue":"//android.widget.Button[@text='QUICKSTART']","id":1,"projectId":1}],"error":3,"id":4,"parentId":0,"platform":1,"projectId":1,"sort":4,"stepType":"click","text":""}}],"device":[{"agentId":1,"chiName":"","cpu":"arm64-v8a","gear":0,"id":1,"imgUrl":"","manufacturer":"Meizu","model":"M1822","name":"meizu_M1822_CN","nickName":"","password":"","platform":1,"position":0,"size":"1080x2160","status":"ONLINE","udId":"822QEDU5227H3","user":"lrs","version":"8.1.0"}],"cid":2}],"pf":1}
    

    4、动手开始修改代码,在 TestSuitesServiceImpl.java 的 181 行代码修改如下

                        // 发送 test 套件
                        String testJsonStr = "{\"msg\":\"suite\",\"cases\":[{\"gp\":{},\"rid\":14,\"steps\":[{\"step\":{\"caseId\":1,\"conditionType\":0,\"content\":\"\",\"elements\":[],\"error\":3,\"id\":1,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":1,\"stepType\":\"openApp\",\"text\":\"com.example.tablayouttest\"}},{\"step\":{\"caseId\":1,\"conditionType\":0,\"content\":\"\",\"elements\":[{\"eleName\":\"点击 quickstart\",\"eleType\":\"xpath\",\"eleValue\":\"//android.widget.Button[@text='QUICKSTART']\",\"id\":1,\"projectId\":1}],\"error\":3,\"id\":2,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":2,\"stepType\":\"click\",\"text\":\"\"}}],\"device\":[{\"agentId\":1,\"chiName\":\"\",\"cpu\":\"arm64-v8a\",\"gear\":0,\"id\":1,\"imgUrl\":\"\",\"manufacturer\":\"Meizu\",\"model\":\"M1822\",\"name\":\"meizu_M1822_CN\",\"nickName\":\"\",\"password\":\"\",\"platform\":1,\"position\":0,\"size\":\"1080x2160\",\"status\":\"ONLINE\",\"udId\":\"822QEDU5227H3\",\"user\":\"lrs\",\"version\":\"8.1.0\"}],\"cid\":1}],\"pf\":1}";
                        NettyServer.getMap().get(id).writeAndFlush(testJsonStr);
    
                        try {
                            sleep(20000);
                            // 发送 test2 套件
                            String test2JsonStr = "{\"msg\":\"suite\",\"cases\":[{\"gp\":{},\"rid\":20,\"steps\":[{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"BACK\",\"elements\":[],\"error\":3,\"id\":3,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":3,\"stepType\":\"keyCode\",\"text\":\"\"}},{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"\",\"elements\":[{\"eleName\":\"点击 quickstart\",\"eleType\":\"xpath\",\"eleValue\":\"//android.widget.Button[@text='QUICKSTART']\",\"id\":1,\"projectId\":1}],\"error\":3,\"id\":4,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":4,\"stepType\":\"click\",\"text\":\"\"}}],\"device\":[{\"agentId\":1,\"chiName\":\"\",\"cpu\":\"arm64-v8a\",\"gear\":0,\"id\":1,\"imgUrl\":\"\",\"manufacturer\":\"Meizu\",\"model\":\"M1822\",\"name\":\"meizu_M1822_CN\",\"nickName\":\"\",\"password\":\"\",\"platform\":1,\"position\":0,\"size\":\"1080x2160\",\"status\":\"ONLINE\",\"udId\":\"822QEDU5227H3\",\"user\":\"lrs\",\"version\":\"8.1.0\"}],\"cid\":2}],\"pf\":1}";
                            NettyServer.getMap().get(id).writeAndFlush(test2JsonStr);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
    
                        //NettyServer.getMap().get(id).writeAndFlush(result.toJSONString());
    

     5、数据库执行如下sql 

    select * from results where id=14; // 测试结果
    
    UPDATE results set end_time=null, receive_msg_count=0, `status`=0 where id=14; // 恢复测试结果数据
    DELETE FROM result_detail WHERE result_id=14; // 删除上一次运行结果
    
    
    select * from results where id=20 // 测试结果
    
    UPDATE results set end_time=null, receive_msg_count=0, `status`=0 where id=20; // 恢复测试结果数据
    DELETE FROM result_detail WHERE result_id=20; // 删除上一次运行结果
    

    6、web页面运行套件 test  

     

    点击后在agent中就能看到运行test套件后又继续运行test2套件

     3.1、异步 发送 test 套件,判断 test 套件完成后,再发送 test2 套件

    1、在 TestSuitesServiceImpl.java 的 181 行代码再次修改
                        cachedThreadPool.execute(() -> {
                            while (true) {
                                try {
                                    sleep(1000);
                                    Results results1 = resultsService.getById(14);
                                    if (results1.getStatus() > 0) {
                                        System.out.println("==== 运行套件 test2");
                                        // 发送 test2 套件
                                        String test2JsonStr = "{\"msg\":\"suite\",\"cases\":[{\"gp\":{},\"rid\":20,\"steps\":[{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"BACK\",\"elements\":[],\"error\":3,\"id\":3,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":3,\"stepType\":\"keyCode\",\"text\":\"\"}},{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"\",\"elements\":[{\"eleName\":\"点击 quickstart\",\"eleType\":\"xpath\",\"eleValue\":\"//android.widget.Button[@text='QUICKSTART']\",\"id\":1,\"projectId\":1}],\"error\":3,\"id\":4,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":4,\"stepType\":\"click\",\"text\":\"\"}}],\"device\":[{\"agentId\":1,\"chiName\":\"\",\"cpu\":\"arm64-v8a\",\"gear\":0,\"id\":1,\"imgUrl\":\"\",\"manufacturer\":\"Meizu\",\"model\":\"M1822\",\"name\":\"meizu_M1822_CN\",\"nickName\":\"\",\"password\":\"\",\"platform\":1,\"position\":0,\"size\":\"1080x2160\",\"status\":\"ONLINE\",\"udId\":\"822QEDU5227H3\",\"user\":\"lrs\",\"version\":\"8.1.0\"}],\"cid\":2}],\"pf\":1}";
                                        NettyServer.getMap().get(id).writeAndFlush(test2JsonStr);
                                        break;
                                    } else {
                                        System.out.println("sleep 1S =================");
                                    }
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
    

     2、在 TestSuitesServiceImpl.java 的 开头增加

        private ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    

    3、继续在数据库执行sql,清理上一次运行记录

    4、web页面运行 test 套件,从日志中就能看到每隔 1S 查一下数据库直到 test 套件运行完成后,就运行 test2 套件。

     四、配合修改 agent 

    4.1、保持 Appium server  

    1、复制 org.cloud.sonic.agent.common.maps 目录下的 HandlerMap.java 为 HandlerRunSuiteMap.java 备用。这个 map 用来保存 androidStepHandler

    2、将 AndroidTestTaskBootThread.java 文件的201行 androidStepHandler.startAndroidDriver(udId); 改为

                    if (HandlerRunSuiteMap.getAndroidMap().get(udId) == null) { // 按照设备 uuid 查找之前是否初始化过
                        androidStepHandler.startAndroidDriver(udId); // 没有初始化
                        HandlerRunSuiteMap.getAndroidMap().put(udId, androidStepHandler); // 初始化后 put 到 map 中
                    } else {
                        androidStepHandler = HandlerRunSuiteMap.getAndroidMap().get(udId); // 之前初始化过,从 map 中取出使用
                    }  

    3、246 行将 androidStepHandler.closeAndroidDriver(); 注释。这句的意思是在一个 suite 结束后关闭 appium

     4、再次运行 test 套件,在日志中看到 先后执行test 套件和 test2 套件,但只启动了一次 appium

     5、到此同一个设备无论 runSuite 多少次,也只会启动一个 appium 实例,这样就解决多次调用需要等很久的问题。不过这里有一个bug,我将 closeAndroidDriver 注释了,这样一直没有释放 appium,后续还要加一个 step ,启动 appium、停止 appium

     4.2、修改上报运行结果,将多个case的结果报到一起

    1、在 LogUtil.java 的 55 行添加如下代码

            if (resultId == 20) { // 如果是 test2 套件
                resultId = 14; // 临时将 test2 套件的id 20 改为 test套件的id 14
            }
    

     2、再次运行 test 套件,在日志中看到无论 test 套件还是 test2套件上报的 rid 都是14

    3、在数据库中看到, 只有 result_id 为 14 的上报

    4.3、修改下发 suite 数据,将多个case的结果报到一起

    上边的方法虽然可以都报道 result_id=14 的结果里,但那么改太蠢。其实可以通过修改下发 suite 的方式让他报到一起。
    1、还原 agent 下  LogUtil.java 代码

    2、还记得上边通过修改server 中的 TestSuitesServiceImpl.java 文件,实现 test 套件运行完后运行 test2 套件的吗?就是改哪里。将 197 行的 rid:20 改为 14

    3、重起 controller 后,再次运行 test 套件,同样在日志里看到两个套件上报的都是 rid=14,数据库中看到了也是 7 条 rid=14 的记录

    所以后边只要修改下发数据,即可实现多个设备运行同一个 case ~~~ 

    4.4、增加 step ,关闭 appium

     1、在 agent 的 AndroidStepHandler.java 的 1485 增加如下代码

                case "closeDriver" : // 增加 closeDriver 的 step
                    closeAndroidDriver(); // 调用关闭 appium 的方法
                    break;
    

     2、在 AndroidStepHandler.java 的 192 行的 closeAndroidDriver 方法中增加 清除 HandlerRunSuiteMap 代码

                    HandlerRunSuiteMap.getAndroidMap().remove(log.udId); // appium 关闭成功后清除对应 udid HandlerRunSuiteMap
    

    3、在server 中的 TestSuitesServiceImpl.java 文件的 197 行中增加关闭 appium 的测试步骤,如下

    {"step": {"caseId": 10,"conditionType": 0, "content": "", "elements": [], "error": 3,"id": 5,"parentId": 0,"platform": 1,"projectId": 1,"sort": 5,"stepType": "closeDriver","text": ""}} 

    完整 test2JsonStr 如下

    String test2JsonStr = "{\"msg\":\"suite\",\"cases\":[{\"gp\":{},\"rid\":14,\"steps\":[{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"BACK\",\"elements\":[],\"error\":3,\"id\":3,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":3,\"stepType\":\"keyCode\",\"text\":\"\"}},{\"step\":{\"caseId\":2,\"conditionType\":0,\"content\":\"\",\"elements\":[{\"eleName\":\"点击 quickstart\",\"eleType\":\"xpath\",\"eleValue\":\"//android.widget.Button[@text='QUICKSTART']\",\"id\":1,\"projectId\":1}],\"error\":3,\"id\":4,\"parentId\":0,\"platform\":1,\"projectId\":1,\"sort\":4,\"stepType\":\"click\",\"text\":\"\"}},{\"step\": {\"caseId\": 10,\"conditionType\": 0, \"content\": \"\", \"elements\": [], \"error\": 3,\"id\": 5,\"parentId\": 0,\"platform\": 1,\"projectId\": 1,\"sort\": 5,\"stepType\": \"closeDriver\",\"text\": \"\"}}],\"device\":[{\"agentId\":1,\"chiName\":\"\",\"cpu\":\"arm64-v8a\",\"gear\":0,\"id\":1,\"imgUrl\":\"\",\"manufacturer\":\"Meizu\",\"model\":\"M1822\",\"name\":\"meizu_M1822_CN\",\"nickName\":\"\",\"password\":\"\",\"platform\":1,\"position\":0,\"size\":\"1080x2160\",\"status\":\"ONLINE\",\"udId\":\"822QEDU5227H3\",\"user\":\"lrs\",\"version\":\"8.1.0\"}],\"cid\":2}],\"pf\":1}";
    

     4、重起后重新运行 test 套件,这时候在日志里就看到运行 test 套件的时候启动了 appium,在 test2 套件运行完成后运行了关闭了 appium

    到此,基本实现了多个设备完哼一个测试用例的需求了。但只是演示代码,还不能用于生产,比如:

    1、创建appium的方法不太好,可以参考远控的测试用例执行的方案做

    2、关闭 appium 的方法就不太好

    3、还有很多异常处理的地方没有关闭 appium 等等

    4、在上边的 “处理路线图” 中讲的,web页面还有很多体力活工作。

      

     
  • 相关阅读:
    ASP.NET网页动态添加数据行
    动态生成自定义控件ascx如何给ascx传值
    读取txt文件将文本行组合成特定格式
    怎样对文本文件内的文本行排序
    动态变更Repeater控件HeaderTemplate列名
    减少代码冗余,封装你的程序
    在Repeater控件的OnItemDataBound获取数据源记录总数
    查看服务器系统资源(cpu,内容)利用率前几位的进程的方法
    Nginx反向代理+缓存开启+url重写+负载均衡(带健康探测)的部署记录
    gerrit代码简单备份方案分享
  • 原文地址:https://www.cnblogs.com/rslai/p/16412655.html
Copyright © 2020-2023  润新知