前言

做或不做?

最新在做一些移动端 UI 自动化的工作,已经有一些成果.本次记录一下如何从 0 搭建 UI 自动化框架.

UI 自动化在初创公司都不太被看好,原因有以下几点:

上述几点,目前移动端 UI 自动化在中小公司还没有开展或者开展的不是很好.但是随着项目发版的频率越来越高,每次发版前回归核心功能的手工测试工作量越来越大.

带来的是问题是,每次临近发版本,开发每次 build 一个包,测试同学就要对所有功能 check 一遍,毕竟临近发版不敢仅回归改动的地方,要回归所有功能.

所以移动端 UI 自动化还是有一定的存在必要的,只不过看公司项目的发展,一般来说项目较为稳定做比较合适.

误区

关于 UI 自动化能带来多少价值,这个话题一直比较有争议.

误区有以下几点:

价值

体现 UI 自动化的价值有以下几点:

框架选择

接触 UI 自动化也大概有 3 年左右时间了,从 Robotium、Appium、Macaca 到最近比较火的 atx 都有接触过.但是 Appium 在解决平台兼容性、使用规模大、文档较多相比其他框架还是有较大的优势.

当然框架的选择也是因人而异,没有绝对好和坏.

开发环境

框架设计

先附上一张模块图

兼容

这里指的兼容性是要兼容 Android 主流系统和 iOS 主流系统.

比如 Android 在大于等于 7.0 系统,底层引擎需要使用 uiautomator2,不然在获取元素会存在问题.

比如 iOS 底层使用是 xcuitest,wda 是和 xcuitest 进行通信.但是只有大于等于 9.3 版本底层才使用的 xcuitest.

综上在框架设计前期,不仅需要考虑单点系统,而是需要考虑框架的能兼容多少设备版本.

复用性

现在移动端都是做 Android 和 iOS 两端,所以 UI 自动化也需要写两端的代码.做 Android 和 iOS 唯一不太一样的是元素定位不同,其他 driver 实例和公共方法调用,基本上可复用的.

后续维护

好的框架设计可以减轻后续维护成本,这里最常见的是 PO 封装,使模块代码和测试代码分离.

之前写的 PO 设计文章 https://testerhome.com/topics/15717

页面建模

把 app 每个页面或者多个关联的页面抽象出一个 page

在建模页面中,appium 提供页面工程模式 (PageFactory)

PageFactory.initElements(new AppiumFieldDecorator(driver), HomePage);

需要driver参数和当前类参数初始化当前页面

元素操作

元素定位

appium 提供注解的方式声明元素,并且声明显示等元素时间

@FindBy( xpath = ".//*[@text='发现']")
@WithTimeout(time = Timeout,chronoUnit = ChronoUnit.SECONDS)
public AndroidElement HomeBtn;

需要注意的是 iOS 元素定位的方式不是很多,优先用 accessibility,万不得已采用 xpath,xpath 定位比较慢.

元素操作

在上边定义元素,可以直接使用 click 或者 sendkeys 操作

把 homeTab 封装成一个小方法,可在其他 page 或者 case 中调用

/**
 * 首页tab
 */
public void homeTab() throws InterruptedException {
    HomeBtn.click();
    log_info("点击首页");
}

用例编写

用例编写应该有几个原则:

@BeforeMethod
    public  void  setup() throws IOException, InterruptedException
    baseDriver = driver.preiOSDriver();
    }

case 应该直接调用 page 中的方法,让 case 变得更简洁

/**
 * 测试点:首页-商城
 */
@Test(enabled = true)
public void  test_shop() throws InterruptedException {
    homePage.homeShop();
}

断言

使用 testng 的 Assert 类,可以封装一些简单的断言.

/**
    * 断言页面包含某个的元素
    */
   public void assertContain(String text) throws InterruptedException {
       PageSource = this.driver.getPageSource();
       if (PageSource.contains(text)){
           Assert.assertTrue(true);
       }else {
           Assert.assertFalse(true);
       }
   }

   /**
    * 断言页面包含element
    * by:元素
    */
   public void assertContainBy(By by){
       if (driver.findElements(by).size() > 0){
           Assert.assertTrue(true);
       }else {
           Assert.assertFalse(true);
       }
   }

需要注意的是,点击页面跳转以后,如果立即断言会断言失败,因为页面跳转一般是由 1~3 秒的页面加载,这个时候元素还没有呈现加载处理,如果获取页面元素进行断言必然会抛异常.

解决方法大致可以在每个前言加等待时间或者设置断言超时时间

失败截图

如果 case 中使用了断言方法,在运行多条 case 以后,肯定是关心 case 的成功或者失败,失败具体是什么原因或者当时失败的页面状态.

testng 框架 TestListenerAdapter 类提供了成功、失败等状态的监听方法

那么可以写个类集成 TestListenerAdapter 方法,扩展 onTestFailure 方法.比如实现失败截图,可以使用 base64 方法把图片输出到测试报告中

测试报告

关于选用测试报告插件,testng 提供了测试报告,但是不太美观.也可以使用 reportng,也仅仅比 testng 稍微好看点.

推荐两个比较好的测试报告框架,Allure 和 extentreports.我目前使用的是 extentreports,大致生成测试报告如下

运行

因为使用的 maven,在 pom 中使用了 maven-surefire-plugin 插件,可以动态传入参数.比如运行平台、设备 id、appium 端口等.

另外已经把 appium server 做成自启动,也不需要额外启动.

外部参数传入执行:

PLATFORM:设备平台 Android or iOS
UDID:设备UDID
APPIUMPORT:appium的端口
WDAPORT:wda的端口
MAILLIST:收件人地址  xxxx@163.com

mvn test -DPLATFORM=Android -DUDID=192.168.56.101:5555 -DAPPIUMPORT=4723 -DBPPORT=4724 -DWDAPORT=8003 -DMAILLIST= xxxx@163.com, xxxx@163.com

持续集成


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