Appium做Android功能自动化测试
前言
做Android端功能自动化已有2年多的时间了,使用过的功能自动化框架有Robotium、Uiautomator、Appium。最近研究自动化case复用的方案,调研了Appium的自动化框架,并将其应用到银行一账通的标版中,本文详细介绍基于Appium的Android功能自动化实战经验。主要包括以下几方面内容:
- Appium框架原理介绍
- Appium框架常用API介绍
- 基于Appium框架的自动化开发环境搭建
- 自动化case开发及分层结构设计
- 自动化测试用例书写规范及注意事项
- 功能自动化接入持续集成方案
Android常用功能自动化框架比较
下表给出Robotium、Uiautomator、Appium三种框架的比较。
了解各个自动化框架的特性,结合产品自身特点,选取一个合适的框架尤为重要。
Appium框架原理介绍
Appium 是一个开源、跨平台的自动化测试工具,用于测试原生和轻量移动应用,支持iOS, Android 和 FirefoxOS 平台。Appium 驱动苹果的 UIAutomation 库和 Android 的 UiAutomator 框架,使用 Selenium 的 WebDriver JSON 协议。下图是Appium在Android端的原理架构图
从图中可以看出,Appium Client是对webdriver原生api的一些扩展和封装,它配合原生的webdriver来使用,二者缺一不可。Appium Server有两个主要功能:首先它作为一个http服务器端,接收从Appium Client发送过来的命令(可以认为是case中的具体操作)。其次,它作为bootstrap客户端,在接收Client的命令后,通过socket方式,把这些命令发给目标android机器的bootstrap来驱动Uiautomator执行自动化操作。关于Appium的工作原理,网上资料很多,不再详细介绍,可参考如下链接
http://www.2cto.com/kf/201410/347851.html
http://www.blogjava.net/qileilove/archive/2014/12/23/421659.html
Appium框架常用API介绍
Appium的API包含在Appium Client中,下表给出不同语言平台对应的Client下载地址
下表给出Java平台的常用API,其他API可自行百度或查看Client源码
Appium元素定位和操作的api是分开的,这点与robotium框架不同,与Google新推出的iOS端功能自动化框架EarlGrey是类似的。关于元素定位的api需要注意:当成功定位到元素时,返回WebElement对象,但是若不能定位到元素,此api直接报错,而不是返回NULL。这对“判断某个控件是否存在”这样的常用操作经常容易出错,比如用如下代码判断登录按钮是否存在。
public boolean isLoginButtonShow(){
WebElement wl = DriverManager.getInstance().findElementById(packagename
+ ":id/login_input_account");
return wl.isDisplayed();
}
当登录按钮存在时,返回true,但若登录按钮不存在时,以上代码并不会返回false,而是在第2行直接崩溃。可通过try catch捕获异常,修改如下:
public boolean isLoginButtonShow(){
try{
WebElement wl = DriverManager.getInstance().findElementById(packagename
+ ":id/login_input_account");
return wl.isDisplayed();
}catch (Exception e){
return false;
}
}
基于Appium框架的自动化开发环境搭建
万事开头难,自动化开发环境的搭建会比较麻烦。以下详细讲解如何在mac os操作系统下,搭建基于Appium的自动化开发环境。
1、Android开发环境搭建(JDK/SDK/AndroidStudio)请自行百度,所需安装包可在如下网页中下载:
http://tools.android-studio.org
Appium for mac安装可参考以下连接:
http://www.cnblogs.com/oscarxie/p/3894559.html
其中appium 的安装,建议使用dmg安装,点击下载安装包。“npm install -g appium”命令行安装亲测一直报错,翻墙了也报错。
2、AndroidStudio中新建android工程AppDemo。
3、创建java module(appium):new—>new module—>java library,此java工程用来开发自动化case。
4、安装Appium Client:创建java module (selenium),libs目录中导入相关selenium包,点击下载selenium压缩包
5、appium module添加selenium依赖:file—>project structure—>modelues选中appium—>dependencies添加selenium module。
6、appium module中,新建java class,开始开发自动化case。至此AppDemo工程目录结构如下所示:
7、启动appium server(命令行或者图形界面启动),注意,启动时Android Settings中请勾选No Reset选项,可防止每次执行case时,都重新安装app,如下所示。
8、case执行,可通过IDE和脚本两种方式执行case
IDE方式:case名右击—>run
脚本方式:auto_run.sh,见下方
#!/bin/bash
#auto_run.sh
source ~/.bash_profile
cd ./AppDemo
gradle clean
gradle build
export CLASSPATH=$APPIUM_HOME/java-client-3.3.0.jar:$APPIUM_HOME/selenium-java-2.48.2-srcs.jar:$APPIUM_HOME/selenium-java-2.48.2.jar:$APPIUM_HOME/junit-4.12.jar:$APPIUM_HOME/hamcrest-core-1.3.jar:$APPIUM_HOME/libs/*
cd ./appium
java -classpath $CLASSPATH:./build/libs/appium.jar org.junit.runner.JUnitCore com.incito.appiumdemo.cases.PersonalCente
其中CLASSPATH需要设置为Appium Client所在路径,
com.incito.appiumdemo.cases.PersonalCenter为case所在类,脚本的执行方式有利于自动化接入持续集成和平台化。
至此基于Appium的自动化开发环境搭建完成,下一节介绍如何开发自动化case及case的分层结构设计。
基于Appium的自动化case开发及case分层结构设计
首先为每条case创建一个公共的基类AppiumTestBase,内含setup和teardown两个方法,以后每条case继承该基类即可。代码如下:
public class AppiumTestBase {
public WebDriverWait webwait;
private AndroidDriver driver;
@Before
public void setUp() throws Exception {
File classpathRoot = new File(System.getProperty("user.dir"));
File appDir = new File(classpathRoot, "apps");
File app = new File(appDir, Config.CURRENT_BANK);
DesiredCapabilities capabilities = new DesiredCapabilities(); capabilities.setCapability("deviceName",Config.DEVICE_NAME); capabilities.setCapability(CapabilityType.BROWSER_NAME, ""); capabilities.setCapability(CapabilityType.VERSION, "5.0.1"); capabilities.setCapability("platformName", "android");
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("udid", Config.DEVICE_NAME);//adb devices获得的值
driver = new AndroidDriver(new URL("http://127.0.0.1:"+Config.APPIUM_PORT+"/wd/hub"), capabilities);
webwait = new WebDriverWait(driver,10);
DriverManager.init(driver);
DriverManager.initWebWait(webwait);
driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
@After
public void tearDown() throws Exception {
driver.quit();
}
setup操作包含导入待测应用apk包,设置与Appium Server连接所需参数,并初始化AndroidDriver对象。以标版登录case为例,其需要继承AppiumTestBase,代码如下:
package com.incito.appiumdemo;
import org.junit.After;
import org.junit.Before;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.File;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import io.appium.java_client.android.AndroidDriver;
public class AccountLogin extends AppiumTestBase{
/**
* case 编号 AccountLogin_001
* ****@throws ****Exception
*/
@Test
public void testAccountLogin() throws Exception {
Account.*getInstance*().gotoLoginPage();
Account.*getInstance*().doLogin();
}
/**
* case 编号 AccountLogin_002
* ****@throws ****Exception
*/
@Test
public void testLoginout() throws Exception {
Account.*getInstance*().gotoGestureLoginPage();
Account.*getInstance*().doGestureLogin(Config.*CURRENT_USERNAME*);
Personal.*getInstance*().doLogout();
}
testAccountLogin Case中,执行了两步操作:进入登录页面;执行登录操作。这两步操作都封装在Account类里面。由此引入自动化case的分层设计框架,如下图:
自动化开发过程中,经常遇到的一个问题是,随着产品的不断更新迭代,APP的UI会不断发生变化,自动化如何去应对这样的变化,如何降低其维护代价。case分层设计主要是基于自动化可维护性的考虑,可维护性是功能自动化最重要的评价指标之一,其直接决定了自动化是否能开展下去。试想case数量达到一定程度时,若没有采用封装、分层的设计思路,极有可能出现“牵一发而动全身”的问题。下图是标版自动化case的分层目录图。
下面以登录case为例,详细了解其分层调用关系。登录case在cases层的AccountLogin类中,其代码上面已经展示,回到testAccountLogin Case的操作,第二步操作为Account.getInstance().doLogin();即在登录页面执行登录操作,其封装在business层的Account类中,代码片段如下:
public void doLogin(){
LoginUiAction.*getInstance*().enterUsername(Config.*CURRENT_USERNAME*); LoginUiAction.*getInstance*().enterPassword(Config.*CURRENT_PASSWORD*); LoginUiAction.*getInstance*().clickLogin();
HomeUiAction.*getInstance*().clickOnCancelGesture();
Assert.*assertTrue*(LoginUiAction.*getInstance*().isLogin());
}
doLogin()方法中执行了“输入用户名;输入密码;点击登录按钮;判断登录是否成功”操作,显然这几步操作都在UI层LoginUiAction类中。我们再查LoginUiAction.getInstance().
enterUsername()方法中做了什么,代码片段如下:
//输入用户名
public void enterUsername(String username){
WebElement wl = DriverManager.*getInstance*().findElementById(packagename +
":id/login_input_account");
wl.clear();
wl.sendKeys(username);
}
enterUsername()方法中,先根据id定位用户名输入框,然后清空输入框,再输入用户名,这些操作都是Appium Client中提供的API。至此演示了登录case的分层调用过程:cases—>business—>ui—>api。按照分层结构,只要业务逻辑不变,case维护任务主要放在ui层,上层无需改动,如此可极大减少case的维护代价。
在doLogin()方法中有一个“判断登录是否成功”的操作,断言操作是自动化测试用例中必不可少的一部分,下面就开始介绍自动化测试用例的书写规范。
自动化测试用例书写规范及注意事项
一条case一般需要包括如下几个要素:
- 数据准备
- 具体操作
- 验证点
- 清楚操作结果
数据准备指提前准备测试账号,假数据等;具体操作得按照业务测试同学提供的文本case来转化;验证点指自动化操作后,UI前后的变化点,比如登录后,首页会出现用户名、总资产、财富分等控件,这些都是验证点;清楚操作结果主要是基于case独立性的考虑,尽可能做到每条case是独立的,这样某条case fail了,也不会对其他case造成影响,当然这样会增加case的粒度,case稳定性会受影响,两者之间可自行平衡。
下面介绍case开发的注意事项:
- 元素定位
- ID(首选)
- 文本
- 控件类型
- 坐标
- UI操作需要合理的sleep
- case独立性
- 封装常用操作
- 注释及case前写明操作步骤
元素定位有ID/文本/控件类型/坐标四种方式,推荐使用ID,因为只要某个控件还存在,其ID变化的可能性很小,若其位置发生了变动,case都无需维护。
下面给出查找元素ID的几种方法:uiautomatorviewer/hierarchyviewer/源码。uiautomatorviewer和hierarchyviewer工具在Android SDK中,配置好环境变量后,直接在命令行输入命令即可打开图形化工具,hierarchyviewer需要手机root,推荐使用uiautomatorviewer。如果有待测应用的源码,也可以通过源码来找ID,当然这种方式比较痛苦,但对熟悉业务及个人成长是有好处的,我最开始就是通过源码来找ID的。
对于一些奇葩的情况,元素ID/文本/控件类型都无法定位时,使用坐标定位绝对好使,但不到万不得已,尽量不用,因为用坐标定位就失去了兼容性,换个手机,case可能会执行失败。
UI操作需要合理的sleep,经常由于网络原因,UI加载需要一定时间,自动化操作过程中需要合理的sleep,sleep时间短了,case会失败,长了又浪费时间。Appium框架中引入了隐式sleep方案,在初始化代码中添加driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);即可,表示每步操作最多等待10s。
case独立性前面已介绍,封装及注释容易理解,不做过多解释。
功能自动化接入持续集成方案
功能自动化一般用于项目集中测试、回归测试、dailybuild等,我们不可能通过IDE手动来运行case,一般可借助于jenkins或平台化的方式来批量执行case。下面介绍如何将功能自动化接入jenkins。
接入jenkins主要用到了其定时和轮询的功能,我们只要准备好构建jar(build.sh)和执行case(execCase.sh)的脚本,放入jenkins的Excute shell模块,然后配置定时或轮询的时间即可。build.sh和execCase.sh可参照第四部分介绍的auto_run.sh。具体执行过程如上图所示:jenkins触发自动化job;拉取构建站最新apk,拷贝至appium module的apps目录下;构建测试工程,生成appium.jar(build.sh);批量执行case(execCase.sh),最后jenkins会输出自动化的执行结果,但是输出结果可视化程度不好,可自行开发生成报告脚本。至此详细介绍了基于Appium的功能自动化开发全过程。
Appium做Android功能自动化测试的更多相关文章
- 用Appium让Android功能自动化测试飞起来
前言 做Android端功能自动化已有2年多的时间了,使用过的功能自动化框架有Robotium.Uiautomator.Appium.最近研究自动化case复用的方案,调研了Appium的自动化框架, ...
- Windows下部署Appium教程(Android App自动化测试框架搭建)
摘要: 1,appium是开源的移动端自动化测试框架: 2,appium可以测试原生的.混合的.以及移动端的web项目: 3,appium可以测试ios.android.firefox os: 4,a ...
- Appium之Android功能脚本
Android功能脚本 注:这里只写了登录和退出功能,以下不提供app的包名,下面我使用的是线上包 准备:1.Eclipse的Java环境:2.Appium环境:3.Android真机一台. 创建一个 ...
- 使用Appium做手机自动化录制问题
最近在使用appium做Android手机自动化脚本录制, 发现点击“tap”时,一直没有用,页面还是不能跳转. 咋办?发愁... 于是看到旁边有个“sendkeys”,那是不是能够直接发送参数不就行 ...
- 用Appium进行android自动化测试
appium是开源的移动端自动化测试框架,可以测试ios,android应用.appium让移动端自动化测试不必限定在某种语言和某个具体的框架:也就是说任何人都可以使用自己最熟悉最顺手的语言以及框架来 ...
- appium+python做移动端自动化测试
1 导言 1.1 编制目的 该文档为选用Appium作为移动设备原生(Native).混合(Hybrid).移动Web(Mobile Web)应用UI自动化测试的相关自动化测试人员.开发人员等提供 ...
- Appium 从 0 到 1 搭建移动 App 功能自动化测试平台 (1):模拟器中运行 iOS 应用
转载:https://testerhome.com/topics/4960 在上一篇文章中,我对本系列教程的项目背景进行了介绍,并对自动化测试平台的建设进行了规划. 在本文中,我将在已准备就绪的iOS ...
- 零成本实现Android/iOS自动化测试:基于Appium和Test Perfect
https://item.taobao.com/item.htm?spm=a230r.1.14.14.42KJ3L&id=527677900735&ns=1&abbucket= ...
- 从0到1搭建移动App功能自动化测试平台(2):操作iOS应用的控件
转自:http://debugtalk.com/post/build-app-automated-test-platform-from-0-to-1-Appium-interrogate-iOS-UI ...
随机推荐
- NGUI本地化
之前在网上的NGUI本地化教程是基于老版本的了,现在用的3.7版本已经改了很多,以前的本地化教程有点不适用了. 1.资源: 本地化的txt文本起名叫做Chinese.txt,到resource目录下新 ...
- The Definitive Guide To Django 2 学习笔记(二) 第二个View 动态内容
“Hello World”只是简单的展现了Django 基本的工作机制,但它不是动态的网页.第二个View我们将创建一个动态的网页,该页面上将展现当前的时间和日期. 该View需要做两件事,第一,计算 ...
- RTT工程管理
一.RTT工程管理 RTT采用SCons管理工程. 本次安装版本:Python-2.7.3.1.exe,python-2.7.11.msi,scons-2.3.1-setup.exe 安装完成后,需要 ...
- Oracle的REGEXP_INSTR函数简单使用方法
REGEXP_INSTR函数让你搜索一个正則表達式模式字符串. 函数使用输入字符集定义的字符进行字符串的计算. 它返回一个整数,指示開始或结束匹配的子位置.这取决于return_option參数的值. ...
- MySql—修改权限
MySQL 赋予用户权限命令的简单格式可概括为: grant 权限 on 数据库对象 to 用户 一.grant 普通数据用户,查询.插入.更新.删除 数据库中所有表数据的权利. grant sele ...
- web中用纯CSS实现筛选菜单
web中用纯CSS实现筛选菜单 本文我们来用纯css实现像淘宝宝贝筛选菜单那样的效果,例子虽然没有淘宝那样强大,不过原理差不多,如果花点心思也可以实现和淘宝一样的. 内容过滤是一个在Web上常见的一个 ...
- Web安全测试(一)-手工安全测试方法&修改建议
常见问题 1.XSS(CrossSite Script)跨站脚本攻击 XSS(CrossSite Script)跨站脚本攻击.它指的是恶意攻击者往Web 页面里插入恶意 html代码,当用户浏览该页之 ...
- 关键字final 分别修饰一个类,一个方法,一个变量,各起什么作用
关键字final 分别修饰一个类,一个方法,一个变量,各起什么作用 解答:final修饰类是不能被继承 fianl修饰方法不能在子类中被覆盖 final修饰变量,称为常量,初始化以后不能改变值.
- 【洛谷】P1040 加分二叉树
[洛谷]P1040 加分二叉树 题目描述 设一个n个节点的二叉树tree的中序遍历为(1,2,3,…,n),其中数字1,2,3,…,n为节点编号.每个节点都有一个分数(均为正整数),记第i个节点的分数 ...
- Python实现生命游戏
1. 生命游戏是什么 生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机.它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞.一个细胞在下一个时刻生死取决于相邻八个 ...