Android手机QQ的UI自动化实践

时间:2022-09-04 09:58:04

本文首发于果的博客园,原文链接:https://www.cnblogs.com/yuxiuyan/p/14992682.html

转载请注明出处。

UI自动化

我们为什么要搞UI自动化

可能很多同学都有疑问:我们写了这么多单元测试,为什么还需要UI自动化测试呢?

按照测试金字塔理论,其实每种类型的测试都有自己的意义,UI自动化的意义就在于更贴近用户真实场景的校验,比如对于手机QQ来说,我们需要确保主流程的真实链路是通畅的,而单元测试和接口测试很难做到这一点。

我们需要多少用例

UI自动化的意义是验证用户主流程场景,所以UI自动化一定是最最核心的冒烟用例,针对UI自动化追求覆盖率是没有意义的。

我们在公司内部的实践表明:

  • 对于增长型业务,这种业务产品变化快,重构需求多,那么代码类UI自动化建议占比5%—20%

Android手机QQ的UI自动化实践

  • 对于稳定型业务,这类业务产品功能稳定,大改版需求少,那么代码类UI自动化建议占比10—30%

Android手机QQ的UI自动化实践

Android手机QQ的手工系统测试用例有7000多条,我们对这些用例优先级进行了排序,针对P0用例,再排除掉一些难以自动化的用例,定下了400余条的目标,约为6%。

自动化框架

调研Android自动化框架

Android手机QQ的UI自动化实践

按照部门的要求,我们本次的自动化需要用到同源(同代语言同工程)的形式,而且针对手Q很多复杂的场景,我们需要稳定复现,所以我们排除了QTA、Appium、AirTest等框架,最终选择了UiAutomator。

UiAutomator是Google官方提供的同源测试框架,它的底层使用了Android的系统级服务AccessibilityService,关于这一块的介绍,可以看文章:《从Android手机的抢红包插件说起 》

官方文档传送门:UI Automator | Android Developers

封装模式

在上一步环节中,我们虽然确定了自动化框架,但是框架只提供底层的驱动能力,如果无统一封装模式进行规范,随着用例的增多会变得难以维护,所以我们需要一个统一模式来封装细节,可以使 testcase 更稳健,不需要大改动。即我们需要对UiAutomator API进行二次封装。

业界最常见的一种封装模式是Page Object模式,“页面即对象”。这种封装模式把一个页面看做一个对象,把页面上的控件(按钮、图片等)元素当做对象的属性,把对页面上的控件操作(如点击某按钮)当做对象的方法。这种封装模式的优势是维护简单,对于页面的某些改动,只需要维护该页面对应的对象。劣势是代码复用率较低,不利于大规模铺量

还有一种常见的封装模式是Scene模式,“场景化封装”。这种封装模式就是按照用例的场景,也不需要API的二次封装,简单粗暴去实现。这种封装模式的优势是简单粗暴,可读性高。劣势是代码复用率低,十分冗长

我们的痛点是,需要快速铺量,那按照用例场景,所见即所得的代码方式,的确是很快,但是我们需要对该模式规范化。结合测试用例的3A原则(Arrange、Act、Assert),我们创造了一种新的封装模式QTS(QQ Testcase Service)。

自动化框架QTS

我们在写测试用例的时候,是按照用户角度,从一个个控件元素触发,经过一个个场景页面,最终验证某一个结果。那为什么不直接把上面的元素触发(Action)、场景页面(View)、验证(Check or Assert)自动化呢?这样就可以快速实现任意一个用例了,因为自动化代码和测试用例的文字描述是一一对应的。

QTS是Scene模式的进一步改进。我们抽象出测试用例的3A,抽象出控件动作(Action)、页面元素(View)、断言(Check)这三个最基本接口,同时因地制宜,结合手Q复杂的环境,又抽象出场景流(Workflow)、环境(Env)接口。

TestBase是全部测试用例类的基类,包含了测试用例的一些通用属性和方法。它的属性包括单例模式的action,env,check,qqAcount等,方法包括登录QQ,初始化手Q环境等。所有的测试用例类都继承这个类。

Android手机QQ的UI自动化实践

  • Action:基本操作事件的接口,在该接口中,负责封装实现所有的动作事件,比如点击、长按、输入框输入、滑动等事件。底层设备驱动能力由UiAutomator框架的uiDevice类提供。
void click(String control);   // 点击某个控件
void longClick(String control, long clickTime); // 长按某个控件
void swipeUp(int steps, int swipePixelLength); // 页面上滑
…… ……
  • View:控件定义与查找接口,在该接口中,负责封装所有的设备可检索的控件元素,并提供检索方法。底层的定义能力由UiAutomator的BySelector类提供。
/**
* 获取control的对象
*
* @param controlName control的中文定义
* @return UiObject2对象
*/
UiObject2 getControl(String controlName);
/**
* 获取指定instance以及id的Object(适用于页面中有多个控件拥有相同id的情况)
*
* @param controlName control的中文定义
* @param instance 控件的数值顺序
* @return UiObject对象
*/
UiObject getInstanceControl(String controlName, int instance);
…… ……
  • Check:断言接口,之所以没有叫Assert,是为了和Juint的Assert做一个区分。在该接口中,提供了对于检测元素存在、不存在、判真、判假等方法。底层断言能力由Junit的Assert提供。
/**
* 检查控件是否存在
*
* @param control view层封装的控件中文描述或者控件唯一text
*/
void exist(String control); /**
* 检查控件是否不存在
*
* @param control view层封装的控件中文描述或者控件唯一text
*/
void notExist(String control); /**
* 检查是否成功登陆
*
* @param id 消息tab的feeds数id,用来判断消息tab是否完全家在成功
*/
void loginSuccess(String id);
…… ……
  • Env:环境接口,因为手Q复杂的环境,所以特意把环境相关的方法抽象出来,同时在该接口中,还有直接实现后台接口的方法,比如加好友的接口、删除好友的接口、一键建群的接口、一键销毁群的接口等。
/**
* 创建群(无需验证信息), 测试账号需要添加oidb
*
* @param uin 需要创建群的群主uin
* @param groupName 需创建群的名字
*/
Response createGroup(String uin, String groupName);
/**
* 一键加好友
*
* @param fromUin 申请加好友的号码
* @param friendUin 需要添加的uin
*/
Response addFriend(String fromUin, String friendUin);
…… ……
  • Workflow:场景流接口。这个理解比较困难,可以认为它是一个多view的操作集合,主要提供场景流的一些方法,比如一键进入聊天页面(手Q中称之为“AIO”)等。
/**
* 通过接口进入AIO
*
* @param uin 群或C2C的uin
* @param uinName 名称
* @return 执行状态
*/
void openAIO(ActivityTestRule<splashactivity> activityTestRule, QQAppInterface app, Long uin, String uinName);

此外,QTS还提供了一些通用能力,比如log等。

编写测试用例

有了QTS,根据测试同学提供的用例来自动化,就变得简单明了。比如某一个测试用例,需要打开手Q钱包页面,检查充值记录。那么利用QTS,就可以完全模拟用户的手工操作,一步步实现用例。

public class AccountTest extends QTSBase {
// 1. 实现手Q钱包进入的workflow
private final QWalletHomeWorkflow qWalletHomeWorkflow = new QWalletHomeWorkflow();
@Override
public void setView(ArrayList<view> viewList) {
// 2. 添加这个用例涉及到的view页面
viewList.add(new MsgTabView());
viewList.add(new QWalletHomeView());
viewList.add(new QWalletAccountView());
}
// 3. 登录手Q
@Before
public void setUp() throws Exception {
loginQQ(activityTestRule, "qq_uitest");
}
// 4. 编写用例
@Test
@CaseAdditionInfo(tags = {"FT=UI自动化", "模块=QQ钱包", "功能=点击Q币", "测试分类=功能",
"测试阶段=全用例", "管理者=neoyu", "用例等级=P0", "用例类型=1",
"被测函数=null", "用例描述=在账户页点击Q币", "版本=850", "手工用例ID=748878937694273536"
})
public void testShowQCoinRechargeRecord() {
// 5. 这里的代码所见即所得,简单明了,即使不注释,也可以很快读懂用例的步骤与含义,也方便责任人交接
qWalletHomeWorkflow.openQWalletAccount();
action.waitThenClick("账户页Q币个数");
check.exist("充值记录标题");
check.exist("全部交易");
}
}

录制回放工具

背景

光有上面的QTS框架,虽然写自动化用例已经很快了,但是还是达不到部门的目标(毕竟部门要实现在半年内把手Q欠了多年的技术债补齐),在这种压力下,就必须引入新的工具、新的方法。我们调研了公司内外的方案,最终使用了公司内部开源的录制回放工具。录制回放工具是一个通过手工录制,然后回放校验断言的自动化测试工具。

基本原理

Android手机QQ的UI自动化实践

其实核心原理并不复杂,在录制的时候记录下元素、对应的操作、网络与IO数据,在回放的时候mock数据并回放操作。这里要注意,因为涉及到复杂mock与元素的处理,这个工具是侵入式的,需要维护一个手机QQ(录制回放版本)的打包流水线。

实践

1.手工测试用例

目前手Q的全部测试用例都托管在公司内部的tcm平台上,我们的目标就是把tcm平台上,部分P0用例实现自动化。

对于新功能,由外包同学或者测试同学来录入手工用例,确定优先级。我们之后会针对P0用例,考虑自动化。

2. 编写自动化用例

直接利用QTS编写。需要注意的是,这里的CaseAdditionInfo是利用了公司内部的终端自动化测试平台的能力,在CI系统上配置了流水线,会扫描代码仓库,当扫描到这个注解的时候,就会认为这是一个测试用例,然后把这个测试用例的相关信息上传到该平台。

@Test
@CaseAdditionInfo(tags = {"FT=UI自动化", "模块=群", "功能=群活跃排行榜", "测试分类=功能",
"测试阶段=全用例", "管理者=stancheng", "用例等级=P0", "用例类型=1",
"被测函数=null", "用例描述=活跃排行榜为空", "版本=850", "手工用例ID=730700977215709184"
})
public void testEmptyGroupRankingList() {
// 打开群设置
aioWorkflow.openGroupAio(activityTestRule, app, "272329539", "UITest群4");
// 等待一下,群荣誉需要拉取配置
delay(5);
// 打开群荣誉
aioWorkflow.enterChatSetting();
boolean isSuccess = groupSettingWorkflow.enterHonorSetting();
// 如果失败,那就返回再进入
while (!isSuccess) {
action.back();
aioWorkflow.enterChatSetting();
isSuccess = groupSettingWorkflow.enterHonorSetting();
}
// 检测是否存在虚位以待
delay(5);
check.exist("虚位以待");
}

3. 管理自动化测试用例

自动化用例的管理主要依托终端自动化测试平台,在这里实现了用例的解析、上传、流水线绑定、测试用例集管理、数据看板等操作。

提升稳定性的一些方法

1. 后台接口代替UI操作

手Q里面很多场景都是超级复杂的,比如加好友后自动发消息,加好友这个操作本身就很复杂,如果场景累加的话,那UI自动化的运行时间将大大延长,并且每多一个view就增加检索失败的风险。所以这时候,就需要和开发同学协商,把加好友这一步,做成接口,在客户端可以直接调用。这些接口,我们统一封装在Env接口类中。

2. 重试机制

UI自动化用例中,偶现某个元素或操作事件没有生效的情况,这和设备有很大关系。在没有更多预算的情况下,就需要增加一些重试,比如下列用例,其中的retryCount就是一个重试机制:

public void clickFile() {
clickPlusBtn();
UiObject2 object2 = uiDevice.findObject(By.clazz(Constants.CLASS_RADIO_BUTTON));
int retryCount = 0;
while ( object2 == null && retryCount < 5) {
retryCount ++;
clickPlusBtn();
object2 = uiDevice.findObject(By.clazz(Constants.CLASS_RADIO_BUTTON));
}
plusAreaClick("文件");
}

3. 等待

远程设备经常会出现网络慢的情况,这时候加载元素就很慢,对于UI自动化来说,就会报“Contorl not found”的错误。这种非人力、非代码问题,我们一般是使用wait()或sleep()方法增加对加载页面的等待时间。

结语

限于笔者的经验,以及部门时间压力,很多东西没有时间好好雕琢。UI自动化是一个很广大的领域,但是近些年,业界对于UI自动化的研究并没有很深入,在我们实际的生产活动中,也是发现了UI自动化的很多缺点:维护成本高、设备依赖程度高等,所以每个项目都需要因地制宜,思考UI自动化对于项目的意义

我的博客即将同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=1fthcjmf2bsx2

Android手机QQ的UI自动化实践的更多相关文章

  1. Xposed截获 Android手机QQ密码

    0x00 前言 Xposed框架是一款修改系统框架服务的软件,通过它许多功能强大的模块得以实现,且不冲突地同时运作,自从Xposed框架发布以来,安卓手机的可玩性日益激增,最近很闲很蛋疼,研究下截获A ...

  2. Android手机QQ文件夹解析

    注:切勿修改手机QQ文件夹,以免造成不必要的使用问题及无法修复的数据丢失] 安卓手机QQ tencent文件夹解析 QQ下载的聊天背景:tencent→MobileQQ→system_backgrou ...

  3. UI自动化测试之Airtest

    官方文档: https://airtest.doc.io.netease.com/ 本文我们讲解下Airtest的使用,主要学习目标有以下几点: (1)认识Airtest (2)了解Airtest能做 ...

  4. 腾讯技术分享:Android版手机QQ的缓存监控与优化实践

    本文内容整理自公众号腾讯Bugly,感谢原作者的分享. 1.问题背景 对于Android应用来说,内存向来是比较重要的性能指标.内存占用过高,会影响应用的流畅度,甚至引发OOM,非常影响用户体验.因此 ...

  5. 腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

    本文来自腾讯前端开发工程师“ wendygogogo”的技术分享,作者自评:“在Web前端摸爬滚打的码农一枚,对技术充满热情的菜鸟,致力为手Q的建设添砖加瓦.” 1.GIF格式的历史 GIF ( Gr ...

  6. 手机APP自动化之uiautomator2 &plus;python3 UI自动化

    题记: 之前一直用APPium直到用安卓9.0  发现uiautomatorviewer不支持安卓 9.0,点击截屏按钮 一直报错,百度很久解决方法都不可以,偶然间看见有人推荐:uiautomator ...

  7. &lpar;appium&plus;python&rpar;UI自动化&lowbar;02&lowbar;appium启动手机app

    前提:需先安装配置好appium+python自动化环境,已配置好环境的小伙伴可以参考以下步骤启动Android app,具体步骤如下: 一.USB连接手机 (1)手机USB连接电脑 (2)手机打开开 ...

  8. UI自动化技术在高德的实践

    一.背景汽车导航作为ToB业务,需要满足不同汽车厂商在功能和风格上体现各自特色的需求.针对这种情况,传统的UI开发方式,基本上是一对一的特别定制.但是这种方式动辄就要500~600人日的工作量投入,成 ...

  9. 微信小程序UI自动化:实践之后的记录01-选择工具&sol;框架

    目录 1. 前言 2. 工具/框架/库选择 2.1 miniprogram-automator官方介绍(摘自官方哈) 小程序自动化 特性 2.2 minium官方介绍 特性 3. 如何选择 4. 对应 ...

随机推荐

  1. Mongodb Manual阅读笔记:CH8 复制集

    8 复制 Mongodb Manual阅读笔记:CH2 Mongodb CRUD 操作Mongodb Manual阅读笔记:CH3 数据模型(Data Models)Mongodb Manual阅读笔 ...

  2. Why NSAttributedString import html must be on main thread&quest;

    The HTML importer should not be called from a background thread (that is, the options dictionary inc ...

  3. Factory Girl使用

    1.使用Rspec,详见http://www.cnblogs.com/fanxiaopeng/p/3563772.html 2.在gemfile中添加 #Gemfile group :developm ...

  4. Java中Runnable和Thread的区别(转)

    http://developer.51cto.com/art/201203/321042.htm 第一种方式:使用Runnable接口创建线程 第二种方式:直接继承Thread类创建对象 使用Runn ...

  5. vim简单命令教程-firstblood

    你想以最快的速度学习人类史上最好的文本编辑器VIM吗?你先得懂得如何在VIM幸存下来,然后一点一点地学习各种戏法. Vim the Six Billion Dollar editor Better, ...

  6. Spring Cloud学习笔记-007

    声明式服务调用:Spring Cloud Feign Feign基于Netflix Feign实现,整合了Spring Cloud Ribbon和Spring Cloud Hystrix,除了提供这两 ...

  7. 【&period;net】在ASP&period;NET中,IE与Firefox下载文件名中带中文汉字的文件,文件名乱码的问题

    #问题:客户端为ie或Firefox,服务端为asp.net时,下载文件名中包含中文汉字时,下载下来的文件的文件名是乱码: #解决方案: 示例代码:下载名称中带汉字的文件: public void P ...

  8. 自学Linux Shell19&period;1-gawk程序基础特性

    点击返回 自学Linux命令行与Shell脚本之路 19.1-gawk程序基础特性 linux世界中最广泛使用的两个命令行编辑器: sed gawk 1. gawk概念 awk是一个强大的文本分析工具 ...

  9. java 反射获取类的静态属性值

    public class AppTest { private NodeClass nodeClass; public static String hehe = "hehe"; pu ...

  10. bestcoder&num;23 1001 Sequence

    Sequence Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...