最近给 Sonic 提交了一个小 PR,分享一下我的踩坑记录

Sonic 官网

需求背景

作为从 Sonic 开源就开始投入使用的我们来说,Sonic 还是很让我们佩服的,无论是迭代速度和维护力度都是我们从未见过的。
我们的 App 属于混合应用,基于原生 +H5 的形式开发。上一年开始我们开始投入到 UI 自动化里面,刚好 Sonic 提供了自动切换 WebView、切换 Handle 的操作,也能操作 H5 控件,但是更复杂的操作得要用 Sonic 的 自定义脚本 去完成。

例如: 将设备中心的区域往某个方向拖拽多少像素,代码太长就不放出来了

例如: 将 WebView 页面滚动到目标控件到顶部

import org.openqa.selenium.JavascriptExecutor;

def scrollToView(selector,pathValue) {
  try{
    JavascriptExecutor jsExe = (JavascriptExecutor) androidStepHandler.chromeDriver;
    jsExe.executeScript("arguments[0].scrollIntoView();", androidStepHandler.findWebEle(selector,pathValue));
  } catch(Exception e){
      throw e;
  }
}

使用的时候直接使用就可以了

scrollToView("xpath","//div[text()=\"xxx\"]")

落地下来 UI 自动化效果还是不错的。自定义脚本初衷是作者为了开放给用户使用更多自定义的能力(甚至直接调用 Agent 本身许多方法),但是对于我们业务测试的小伙伴来说,还是 Sonic 直接提供 UI 操作更方便呀,这样就可以专注到业务测试中。于是想着,给 Sonic 直接提个 PR 参与建设!

准备工作

  1. 我们从 Github 将 Agent 源码拉下来
git clone git@github.com:SonicCloudOrg/sonic-agent.git
  1. 配好 jdk、maven 后,等待 maven 下载依赖
  2. 配置下 agent 服务相关的设置,如果本地 Agent 连生产的 Server,需要将 pom.xml 的 releaseMode 改为 true 后刷新
  3. 启动 AgentApplication 就可以了

官网详细的文档 传送门

开始开发

中心滑动需求

我们分两步:

  1. 获取设备分辨率
  2. 计算中心坐标后使用 Sonic 的触控方法操作

获取设备分辨率

其实 Sonic 已经封装得差不多了,我们在 org/cloud/sonic/agent/tests/handlers/AndroidStepHandler.java 下随便找个地方新开一个方法

public void swipeByDefinedDirection(HandleContext handleContext, String slideDirection, int distance) throws Exception {

}

首先我们要获取设备当前屏幕尺寸,AndroidDeviceBridgeTool中有现成方法,于是乎:

public void swipeByDefinedDirection(HandleContext handleContext, String slideDirection, int distance) throws Exception {
        String size = AndroidDeviceBridgeTool.getScreenSize(iDevice);
        String[] winSize = size.split("x");
        int width = BytesTool.getInt(winSize[0]);
        int height = BytesTool.getInt(winSize[1]);
        log.sendStepLog(StepType.INFO, "", "设备分辨率为:" + width + "x" + height);
}

这样宽高我们都拿到了,中心坐标也就能直接拿到

int centerX = (int) Math.ceil(width / 2.0);
int centerY = (int) Math.ceil(height / 2.0);

注意,这里有一个大坑!

我在将逻辑搬运给 iOS 的时候,SibTool也有一个获取分辨率的方法

SibTool.getSize(String udId)

结果在调试的时候一直滑动没有反应,后来跟作者了解了下,原来是这样

  1. SibTool 获取的这个是设备真实分辨率
  2. 执行 UI 自动化的时候,wda 并不是使用设备的真实分辨率,使用的是设备的逻辑分辨率(不同设备的缩放因子不一样)
  3. 缩放因子在不同设备的收集也有人做出来了 https://zhuanlan.zhihu.com/p/374740958

因此在这个需求里面,获取设备长宽应该用的是

WindowSize size = iosDriver.getWindowSize();
int width = size.getWidth();
int height = size.getHeight();

滑动

滑动事件 Sonic 已经封装好了,支持 APK 触控和 ADB 触控,我们直接调用AndroidTouchHandlerswipe方法就行

try {
      AndroidTouchHandler.swipe(iDevice, centerX, centerY, centerX, targetY);
} catch (Exception e) {
      handleContext.setE(e);
}
handleContext.setDetail("拖动坐标(" + centerX + "," + centerY + ")到(" + centerX + "," + targetY + ")");

最后我们在最下方的runStep中加上 case:

case "swipeByDefinedDirection" -> swipeByDefinedDirection(handleContext, step.getString("text"), step.getInteger("content"));

这样前端传入swipeByDefinedDirection类型和入参就可以了。

WebView 滑动至控件顶部需求

这个就更简单了,直接将之前写好的 Groovy 复制过来就行了,查找控件的方法 Sonic 封装好了,我们只需要将 JavascriptExecutor 的处理加入就行

public void webElementScrollToView(HandleContext handleContext, String des, String selector, String pathValue){
    handleContext.setStepDes("滚动页面元素" + des + " 至顶部可见");
    WebElement we;
    try{
        we = findWebEle(selector, pathValue);
    } catch (Exception e){
        handleContext.setE(e);
        return;
    }
    JavascriptExecutor jsExe = chromeDriver;
    jsExe.executeScript("arguments[0].scrollIntoView();", we);
    handleContext.setDetail("控件元素" + selector + ":"+ pathValue + "滚动至页面顶部");
}

最终代码: https://github.com/SonicCloudOrg/sonic-agent/pull/315/files

前端

前端我个人并不是很擅长,不过作者说最近他会顺便补上,到时候就可以跟我们目前使用的可视化操作一样方便了,直接添加到步骤里就能运行。会跟随 v2.4.0 版本一起发布。

结语

虽然这次 PR 的技术含量并不是很高,但是在跟作者沟通改进的过程中,学习了很多

  1. 项目架构与方法设计模式
  2. 整体框架调度逻辑与测试报告上报逻辑
  3. 对整个平台的可操作性更加透明化了
  4. 在 Github 上提交 PR 的规范与使用
  5. 对 UI 自动化很多实际应用中的小坑都体验到了

很开心也有幸成为了 Sonic 贡献者之一,贡献者证书也在快递路上了,让人期待的精美小礼物~


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