应用宝项目组采用FT(Feature Team)模式,整个项目组分为多个FT,而每个FT又同时有多个需求分支在并行运作着,几乎每天都有多新特性合入主干,项目节奏快、变更频繁,且又希望能够短周期内快速地对外发布新版本,做到快速交付、持续交付。
为了支撑项目组的这种研发模式,测试侧需要在FT分支上及主干上做大量的测试,而其中在FT分支的rebase测试、合流后验证、主干灰度测试等等阶段还包括大量的重复性测试,因此有必要在这些环节加入自动化测试,以持续验证新特性未破坏原有系统。
框架 | Robotium | Espresso | UIAutomator | Appium |
---|---|---|---|---|
支持的API版本 | All | 8,10,15 >= | 16 > | All |
成熟度/活跃度 | 成熟/活跃 | 成熟/Google | 较成熟/活跃 | 较成熟/活跃 |
支持的语言 | Java | Java | Java | Almost Any |
WebView/X5 WebView | 支持/支持 | 不支持 | 不支持 | 支持/支持 |
UIAutomation | API >=18, 支持结合UIAutomation | API >=18, 支持结合UIAutomation | 不支持 | 未知 |
用例执方式/adb依赖性 | adb shell am | 同Robotium | adb shell uiautomator runtest | Client Server发送命令模式 |
主要优点 | 1.通过id识别控件,手机适配好 2.基于Instrumentation,执行速度快、稳定性好 3.可使用Android及被测工程的API | 同Robotium | 跨应用支持好 | 1.不需要对被测应用进行修改 2.脚本支持多种语言进行编写 3.支持跨平台,可用于IOS、Android |
主要缺点 | 1.测试apk签名需要与被测apk一致 2.跨应用能力弱 | 同Robotium | 1.只支API>16 2.需要被测控件有 android:hint等属性 | 1.API4.2以下只支持Selendroid方式 2.一台MAC机只能运行一个Instrument实例 |
执行速度 | 4星 | 4星 | 4星 | 3.5星 |
稳定性 | 4星 | 4星 | 3.5星 | 3.5星 |
通过对比可以发现不同的测试框架各有优缺点,且基本都不能独自满足所有的测试场景,正所谓菜刀、牛刀、剪刀、指甲刀,刀刀皆有自己的用途与适用场景,我们需要根据项目情况及实际的使用场景来选择最适用的工具。
Robotium基于原生Android Instrumentation扩展而来,因此基于Robotium的测试既可以使用Robotium本身的API,还可以使用Android原生的丰富API,可扩展性更强,且基于Robotium的测试在执行速度、稳定性上有一定优势,而应用宝在手机端只有Android版本,也没有跨平台的需求,综合考虑,因此选择了Robotium框架。
3. 环境搭建
测试工程使用了Robotium,采用了的是Android Junit工程,因此需要搭建基础的Android开发环境,包含JDK、Android SDK、Eclipse+ ADT插件 + SVN插件等等开发工具,具体可搜“Android开发环境搭建”搭建基础环境。
(1) 使用Eclipse导入项目;
(2) 配置Build Path;
(3) 配置keystore;
在实际项目中,如果是自家的项目,显然是不希望对被测App进行重签名的,有如下原因:
a) 每日进行测试的包众多,一一进行重签名影响效率;
b) 如微信、应用宝等应用做了签名防护措施,重签名后将导致应用部分功能不可用甚至直接无法启动。
测试工程需要与被测工程签名一致,因此测试工程需要将keystore配置成应用宝的签名。Window——Preferences——Android——Build,如图1所示,点击Browser,选择应用宝的debug.keystore签名,配置完成后,用Eclipse调试时,测试工程打出的apk即是应用宝的签名了,可以测试应用宝对外发布的任何包。
图1.Eclipse中配置自定义签名
(4) 配置编码
新导入工程后,工程可能有许多红点,此时工程任意有注释的java文件,如果注释为乱码则是因为编码不一致导致。此时需要将工程编码设置为utf-8。也可右键选择测试工程,仅设置该工程为UTF-8编码。
工欲善其事,必先利其器,测试工程使用Eclipse作为IDE,而为了编写代码可以更高效,有必要进行一些提高效率的设置。
(1) 配置输入联想
为了提高测试用例的代码编写效率,很有必要配置输入联想,在Eclipse中Preferences——Java——Editor——Content Assist配置输入联想。
图2 Eclipse中配置代码自动提示
其中,Auto activation triggers for java中默认只有.符号,即输入.时才会有代码联想出来,为了充分利用代码联想功能,需要在该输入框中把abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ这26个字母及.号输进去,这样,当键入.号或26个字母时,就会有自动提示,提高代码输入效率。
(2) 配置Eclipse的JVM参数
Eclipse的JVM默认设置参数较小,因此可能造成各种卡慢现像,而我们的开发机配置一般较高,可以通过调整JVM参数充分利用机器资源提高Eclipse运行的流畅度,修改Eclipse安装目录下的eclipse.ini文件即可,具体可参考如下配置(修改-vmargs参数后面的配置):
-vmargs
-Dosgi.requiredJavaVersion=1.6
-Xms2048M
-Xmx2048M
-Xmn656M
-XX:PermSize=512M
-XX:MaxPermSize=1024M
-XX:+UseParallelGC
-XX:CMSInitiatingOccupancyFraction=85
-Xverify:none
-Xnoclassgc
-XX:+CMSClassUnloadingEnabled
-XX:+CMSPermGenSweepingEnabled
-XX:+DisableExplicitGC
(3) 关联源码
a) 关联内引用jar包的源码
导入测试工程后,libs下的Robotium和Uiautomator两个jar使用了properties配置,默认就已关联上了sources目录下的源码,如图3所示:
图3.Eclipse中配置关联private jar
b) 关联外引用jar包的源码
关联外引用jar包的源码,这里主要关联Android SDK中的源码,右键android.jar,进入Java Source Attachment选项,关联sdk中的源码,如图4所示:
图4.Eclipse中配置关联外引用的jar
至此,不论进入的是Android SDK还是Robotium中的class类,均可以查看到其源码实现。
4. Robotium
其中Solo类是主要对外提供各种API的类,Solo类采用中介者模式,持有com.robotium.solo包下的其它类的的实例对象,当我们调用Solo类中的API时,实则大多数是转而调用com.robotium.solo包下其它类的方法。com.robotium.solo包下主要有以下类:
Getter: //提供控件获取相关API
ActivityUtils: //提供Activity相关API
Asserter: //提供断言相关的API
Clicker: //提供模拟点击相关的API
ScreenshotTaker: //提供截图相关的API
Scroller: //提供滚动相关的API
Searcher: //提供控件搜索相关的API
ViewFetcher: //提供控件过滤相关的API
Waiter: //提供控件等待相关的API
WebUtils: //提供Web支持相关的API
Robotium为了简化测试用例的编写,将以上的这些类都置为了protected,对外只提供Solo类,因此,在编写测试用例时,主要实例化Solo类即可。
Robotium为一款支持黑盒测试也支持白盒测试的自动化测试框架,简单易用,提供了获取控件、发送点击事件、断言等等API。
主要API如表2所示:
返回值 | 方法及说明 |
---|---|
View | getView(String id) 根据id获取控件 |
ArrayList<View> | getCurrentViews()获取当前界面或弹框中所有的控件 |
ArrayList<T> | getCurrentViews(Class<T> classToFilterBy, View parent)获取父控件parent下所有控件类型为classToFilterBy的控件 |
void | clickOnView(View view) / clickLongOnView(View view)点击指定的view控件 /长按指定的view控件 |
void | clickOnScreen(float x, float y) / clickLongOnScreen(float x, float y)根据坐标x,y点击屏幕 / 根据坐标x,y长按屏幕 |
void | enterText(EditText editText, String text)在指定的editText中输入文本text |
void | typeText(EditText editText, String text)在指定的editText中键入文本text |
void | drag(float fromX, float toX, float fromY, float toY, int stepCount)从起始x,y坐标滑至终点x,y坐标;通过stepCount参数指定滑动时的步长 |
void | scrollToTop() / scrollToBottom() / scrollUp() / scrollDown()滚动至顶部 / 滚动至底部 /向上滚动屏幕 / 向下滚动屏幕 |
boolean | waitForView(int id) / waitForText(String text)等待指定控件出现 /等待指定文本出现 |
boolean | waitForActivity(String name)等待指定的Activity出现 |
void | takeScreenshot(String name)截图,图片名称为指定的name参数,图片默认路径/sdcard/Robotium-Screenshots/ |
void | finishOpenedActivities()关闭当前已打开的所有的Activity |
void | goBack() / goBackToActivity(String name)点击返回键 / 不断地点击返回键直至返回至指定的Activity |
ArrayList<WebElement> | getCurrentWebElements(By by)通过By根据指定的元素属性获取当前WebView的所有WebElement元素 |
void | clickOnWebElement(By by)通过By根据指定的元素属性点击WebElement |
void | clickOnWebElement(WebElement webElement)点击指定的WebElement |
void | assertCurrentActivity(String message, String name)断言当前界面是否为name参数指定的Activity,若不是将抛出一个带有message提示的Throwable异常 |
通过如上API文档也可以发现,Robotium框架提供的API是有限的,且也只是使用了一小部分Android的API封装而成。使用Android、Java丰富的类库我们可以开发出微信、手Q、应用宝等等众多App,同样地,我们也可以使用这些丰富的类库去扩展测试框架。
因此,选择Robotium测试框架,不只是选择的一个测试框架,而是选择的一种测试模式,即基于Android、基于Junit的测试模式。
(1)uiautomatorviewer
可以使用%ANDROID_HOME%\tools目录下的uiautomatorviewer.bat工具直接获取当前界面的控件结构及其id。
注:uiautomatorviewer只有在Android较高版本(4.3及以上)才能直接获取控件id值。
图5 Uiautomatorviewer查看控件
(2)处理唯一id的控件
如果当前界面该控件id是唯一的,则处理起来很简单,如下:
Button loginBtn = (Button) solo.getView("loginBtn");
solo.clickOnView(loginBtn)
(3)处理id相同的控件
在Android中,列表ListView采用的是Adapter形式,所以列表中的控件id 都是相同的。此时,需要先获取节点控件的父视图,通过父视图再查找相应的子视图。
(4)控件过滤
测试过程中最常见的方式就是控件过滤
API:getCurrentViews(Class<T>classToFilterBy, View parent)
例如想获取某一个区域内的所有文本:
ArrayList<TextView>textViews = holo.getCurrentViews(TextView.class,parentView);
其中parentView为父视图,获取parentView下所有的TextView
WebElement元素获取可以通过Chrome Mobile Emulation模式获取。安装有较新版本Chrome浏览器,按F12即可进入Chrome Mobile Emulation模式。
输入H5页面链接,如:http://xxx.xxx.xxx/index.html
图6.Chrome按F12
如图6所示可以看到’电视剧’拥有class=”classify classify-tv”的唯一属性。
对于有些无法通过PC浏览器打开的H5页面,可以通过Chrome DevTools连接手机端直接进行调试。
使用DevTools需要以下前置条件:
(1)PC端需要安装Chrome 32及以上版本;
(2)USB线连接手机设备;
(3)用于browser debugging:需要Android4.0及以上版本,并安装有Chrome Android版本;
(4)用于app debugging:需要Android4.4及以上版本,并将WebView设置为可调试。
需要注意的是:PC端中的Chrome版本需要高于手机端中的Chrome版本。
App中将WebView设置为可调试,可使用如下代码设置:
if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.KITKAT){
WebView.setWebContentsDebuggingEnabled(true);
}
DevTools详情介绍可参见官网:
https://developer.chrome.com/devtools/docs/remote-debugging#reverse-port-forwarding
然后,在Chrome浏览器地址栏中输入“chrome://inspect/#devices”
打开应用宝,进入含有WebView的页面,例如进入娱乐TAB,如图7所示,可以看到出现了可以inspect的页面,点击’inspect‘按钮即可对该页面进行调试,如图8所示,展示了该页面的元素。
图7.Chrome中使用DevTools
图8.DevTools点击inspect后展示页面元素
知道了元素的属性后,即可调用相应API获取WebElement对象,通过By方式获取:getCurrentTxWebElements(By by);By方式支持通过classname、id、textcontent等方式,如:
holo.getCurrentTxWebElements(By.className(className));
获取到了相应的WebElement对象后,即可对Web元素进行操作:
clickOnWebElement(By by)
clickOnWebElement(WebElement webElement)
waitForTxWebElement(By by,int timeout,booleanscroll)
4.4 断言
(1)Assert中的断言
使用junit.framework.Assert包中的断言:断言条件的true或false、是否为空等等。如图9所示:
图9.Assert中的断言
(2)ViewAsserts中的断言
使用android.test.ViewAsserts包中的断言:包括断言控件是否左对齐、右对齐、父视图是否包含某子视图等等。
图10 ViewAsserts中的断言
5.跨应用(结合UiAutomator2.0)
2015年3月AndroidDevelopers团队宣布了UiAutomator2.0版本的发布,这个版本最重要的就是UiAutomator终于可以基于Instrumentation了,使用Instrumentation test runner即可运行UiAutomator,反过来,也即在基于Instrumentation的test中也能使用UiAutomator。因此测试工程可同时使用Robotium和UiAutomator进行更丰富地测试。
新版的UiAutomator随Android Support Repository发布,可通过SDK Manager下载,以2.1.0版本为例,位于如下所示的路径中:
%ANDROID_HOME%\extras\android\m2repository\com\android\support\test\uiautomator\uiautomator-v18\2.1.0
新的测试支持库基本都是基于AndroidStudio库,文件以aar结尾而非jar结尾,本小节为方便在Eclipse中介绍需要将aar转化成jar。
如图11所示,使用压缩工具打开uiautomator-v18-2.1.0.aar文件,里面的classes.jar文件即是可用于Eclipse的UiAutomator jar包。提取出该classes.jar文件并重命名为方便记忆的jar包文件,导入至使用了Robotium的测试工程即可。
如图12所示,应用宝在通知栏中开启了快捷工具栏,测试此功能时需要开启通知栏,并点击工具栏中的按钮,这样的操作仅通过Robotium框架是无法完成的,此时就可以结合UiAutomator来实现。
UiAutomator发布2.0版本后,可以通过传入Instrumentation对象获得UiDevice对象。UiDevice.getInstance(instrumentation);
通过UiDevice对象可以完成点击Home键、打开通知栏,并通过UiDevice的findObject方法可以根据文本、资源id等等查找控件,并通过UiObject对象完成点击操作。
使用的findObject方法得到的为UiObject对象,此外也可以通过By的方式获取UiAutomator中的UiObject2对象,例如:uiDevice.findObject(By.res("com.tencent.android.qqdownloader","entry_text_1")).click();
UiAutomator2.0还有许多更丰富更强大的功能,这里就不再一一介绍,总之,通过与Instrumentation结合可以方便地在测试工程中完成跨应用的操作,进行更丰富地测试。
注:更多测试用例、测试报告、持续集成相关内容,请见下文《应用宝基于Robotium自动化测试(下)》