Robotium是一个开源的androidUI自动
测试工具,这里不做介绍,可以见:http://code.google.com/p/robotium/
Orange:
Orange 是我们自己开发的运行在PC端的(只支持windows),用于组织
测试用例、自动重签名apk文件、以及操作模拟器、启动运行测试用例、用 例crash以及失败重跑、测试结果收集等功能的一个工具,通过简单的配置,既可以全自动全SDK版本回归运行robotium编写的测试用例。
为什么开发Orange?
为什么要自己开发一个Orange工具,而不直接使用Robotium编写的测试代码来运行测试用例?
我们在Robotium测试用例运行的过程中发现了以下的一些问题:
1、测试用例运行过程中有时候会直接Crash,导致所有测试用例停止,同时收集不到测试结果
2、用例在模拟器上运行,用例经常会出现失败的情况, 但是你再次运行的时候可能就用成功了,存在不稳定性
另外我们希望有下面的功能:
1、我们希望用户编写好以及robotium的测试用例后,能够方便的全自动全SDK版本回归运行测试用例
2、我们希望用户能够方便的指定当前运行哪些测试用例
3、我们希望能够得到一个直观的运行结果报告
当前这里最重要的是在使用Robotium发现的问题,如果你的用例在运行时crash了,导致什么结果也收集不到, 如果你的自动测试用例稳定性太低,经常出现大量的失败用例,那么用户如何来相信你的测试结果,大量失败用例你去分析失败原因也是一个非常耗时的过程,如果 不能很好的解决稳定性的问题,搭建的自动测试框架失败率会大大的提高。
Orange使用步骤:
为了更好的生成测试结果报告,我们这里使用了一个开源的软件android-junit-report,官方地址:https://github.com/jsankey/android-junit-report ,该软件继承重写了
android自带的InstrumentationTestRunner,会自动把测试结果生成到xml文件中。
所以基于Robotium编写测试用例的时候需要额外的进行下面的两部操作:
1、从https://github.com/jsankey/android-junit-report下载jar包,同时加入到测试用例程序中
2、修改测试程序的AndroidMainifest.xml文件,在文件中加入以下配置
<instrumentation android:targetPackage="com.netease.pris(被测应用的包名)" android:name="com.zutubi.android.junitreport.JUnitReportTestRunner" /
接下去就可以开始编写测试用例了,测试用例编写完成后,
1、获取到被测应用的apk文件,以及你的测试程序的apk文件
2、填写orange框架的配置文件,有一些指定的必填配置项
该文件主要是填写需要测试的sdk版本、被测应用的apk位置、被测应用的主包名、测试程序的apk位置、测试结果收件人列表、测试用例的xml文件地址
config.properties
#填写需要测试的android sdk版本,多个的话中间用逗号隔开,不允许为空
target=android-10,android-14
#
被测试应用存放的位置,windows下目录之间用"\\"来分割,不允许为空
apkpath=D:\\PRIS\\1.3.1\\pris_generic_1.3.1.apk
# 测试程序存放的位置,windows下目录之间用"\\"来分割,不允许为空
testapkpath=D:\\PRIS\\1.3.1\\PrisUIAutoTest.apk
#
测试程序的主包名,不允许为空 package=com.netease.pris
# 运行结果的收件人列表,多个用户用逗号分割,允许为空 maillists=***@163.com,***@163.com
# 测试用例的配置文件绝对路径,不允许为空 testCaseFile=D:\\TestCaseInfo.xml
3、编写指定需要运行的测试用例文件
这里类似于testng的方式来指定需要运行哪些测试用例,但是为了解决crash以及失败重跑的问题,只支持每个测试方法单独配置,而不支持只填写到测试类级别。
TestCaseInfo.xml
<!-- packageName 填写的是测试程序的主包名,必填项 -->
<classes packageName="com.netease.mobile.autotest">
<!--
指定需要运行的测试类,这些类是在测试程序中编写的,也会安装到模拟器中-->
<class name="com.netease.mobile.autotest.testing.PersonalDataTest" >
<methods>
<!--
这里定义需要运行的具体测试方法,这些都是必填项,如果不存在会报错-->
<include name="test01ShowPersonalData" /> <include name="test02EditPersonalData" /> </methods> </class> </classes> |
4、如何运行测试程序
orange框架会生成一个jar包,当你配置好这些选项后,并且拿到了orange.jar后,直接运行命令
java -jar Orange.jar D: \\config.properties
则orange框架会开始自动为测试应用以及被测应用重签名,按照配置项,创建、启动模拟器、安装apk文件、运行测试用例(crash自动重启模拟器,失败则自动重跑,当前最多跑三次),测试用例运行完成后收集测试结果,发送到指定的收件人。
Orange实现原理:
1.1 自动重签名APK文件
使用Robotium来测试应用的话,需要测试程序和被测程序的签名是一样的,但是一般被测程序都会有自己签名过,所以我们需要实现自动的删除被测程序以及测试程序的签名文件,然后使用统一的key对被测程序的测试程序进行签名。
实现原理:
1、删除apk文件中的META-INF文件夹
使用winrar工具的 winrar d ***.apk META-INF 命令可以实现在不解压apk文件的情况下删除压缩包内指定文件夹
所以这里需要先下载安装winrar工具,另外在电脑的环境变量中配置winrar的安装目录,保证在cmd命令中能够调用到winrar命令
(PS:这里原来是使用的java jar -xvf 命令先把apk解压缩,然后调用删除方法删除掉META-INF,最后使用jar -cvf压缩为apk文件,但是通过jar命令解压缩之后,重签名后的网易阅读apk会出现崩溃的情况,改成了通过winrar工具来实现)
2、使用JARSIGNER命令对apk文件使用统一的key进行重签名操作
3、使用zipalign命令对重签名后的文件进行优化
Orange框架如何使用bat脚本:
1、bat脚本和重签名的key在Orange的jar包中会保存一份
2、运行Orange时,复制bat文件、key文件以及测试应用和被测试应用到temp目录下
3、通过java方法的Process process = Runtime.getRuntime().exec( "cmd.exe /c start " + path); 执行bat文件
4、bat文件会自动把当前目录下的apk文件全部都进行重签名,重签名的apk文件保存到指定目录下
1.2 自动创建、启动、删除模拟器以及安装apk功能
需要在不同sdk版本的模拟器上实现全自动切换运行测试用例,所以需要涉及到创建、启动、删除模拟器以及安装apk文件等功能。
实现原理:
所有这些操作都使用了android自带的android命令来实现的
1、创建模拟器 android create avd --name OrangeAutoTest --target android-7 --force
通过java中调用以上命令实现创建模拟器的操作,如何判断创建模拟器的是否成功,以及失败的话错误信息呢?
Process process = Runtime.getRuntime().exec(cmd);来执行上面的cmd命令行
然后通过process.getInputStream() 和process.getErrorStream()来获取到相应的返回信息,如果有错误信息的话可以再getErrorStream中获取到
说明: 正确情况下getErrorStream能够获取到值的话则抛出异常说明命令执行是时候出错了,但是有些命令的话执行正常,但是getErrorStream也能获取到返回值,所以有些命令需要做特殊的处理。
2、启动模拟器 emulator -avd OrangeAutoTest -sdcard c:\sdcard.img
通过上面的命令启动模拟器,同时加载sdcard,sdcard可以通过mksdcard 20M c:\sdcard.img命令来创建
3、删除模拟器android delete avd -n OrangeAutoTest
4、安装APK adb wait-for-device install -r *.apk
1.3 Crash以及失败用例重跑功能
平时我们使用Robotium编写完一些测试用例的时候经常是直接通过Eclipse运行多个测试用例,或者通过junit编写一个testsuite把需要运行的测试用例加入到testsuite中一次运行多个测试用例。
但是在运行的过程中,我们会发现有时候应用会Crash,导致的结果是测试暂停,所有测试的结果都收集不到。
另外在运行的时候因为模拟器的一些不稳定性,可能会存在某一次用例运行失败的情况,但是再次运行用例就恢复正常了。
为了解决Crash的问题,以及失败用例重跑的问题,我们开发了运行在PC端Orange框架。
实现原理:
1、运行测试用例的时候通过Orange框架控制每次只运行一个测试用例,这样子如果这一个用例运行crash了或者失败的话,我可以再次启动应用重新运行,对其它用例不会有影响。
首先通过Robotium编写的需要运行的测试用例需要配置在xml文件中,按照类似TestNG配置文件的格式来指定这次运行哪些测试用例
<!-- packageName is required--> <classes packageName="com.netease.mobile.autotest"> <class name="com.netease.mobile.autotest.testing.LoginTest"> <methods> <include name="testLogin" /> <include name="testUnLogin" /> </methods> </class> </classes> |
这里要求配置的时候必须配置到具体的方法,我们运行的时候Orange框架会解析这个xml文件,最后组合成以下的一些命令来循环运行测试用例,每次运行一个
adb shell am instrument -e class com.netease.mobile.autotest.testing.LoginTest#testLogin -w com.netease.mobile.autotest/com.zutubi.android.junitreport.JUnitReportTestRunner
adb shell am instrument -e class com.netease.mobile.autotest.testing.LoginTest#testUnLogin -w com.netease.mobile.autotest/com.zutubi.android.junitreport.JUnitReportTestRunner
2、如何判断用例是运行Crash的?
我们还是通过Process process = Runtime.getRuntime().exec(cmd);方法来运行第一步生成的adb命令,然后通过 process.getInputStream();获取到流的返回值,如果返回值中包含"shortMsg=Process crashed"字符的话,说明这个用例运行crash了,那我们使用重试机制
再次运行这个测试用例,如果连续3次都是crash的话,则把crash的信息也写入到返回值中,保证了用例运行Crash的话也能够收集的错误信息。
3、如果判断用例运行失败?
这里使用了JUnitReportTestRunner的一个开源的插件(https://github.com/jsankey/android-junit-report),是重写了官方的InstrumentTestRunner,能够帮我们收集每个用例的执行情况。
每次用例执行结束后,会在模拟器或者真机的指定位置下生成一个Junit-report.xml文件,我们通过adb pull 命令把这个文件保存到PC端,然后Orange框架来解析这个文件,如果结果中包含failure或者error的话说明这个用例运行失败了,则重跑用 例。
如果不包含的话说明用例运行成功,则把刚才取出的xml文件存下来,后续所有用例运行完成整合为一个完整的xml结果文件。
4、如何实现用例重跑?
这个只需要实现一个方法的递归调用就可以,每次运行的时候会查看返回值以及解析xml的返回结果文件,如果存在异常则递归调用该方法,同时会定义一个运行次数的参数,如果大于这个次数的时候不管是够通过都会保存当前最后一次的运行结果。
//调用运行测试用例方法 result = e.run(testCase, testAppPackageName); // 如果出现timeout异常则再运行一次 if (result.equals(EmulatorHelper.timeOutException)) { //如果超出运行次数,则保存错误信息,否则递归运行 if(j > runCount){ addErrorToXml(root, testCase, "adb shell am timeoutexception", beginTime,"TimeOutException"); return result; }else{ return runTest(root, e, testCase, testAppPackageName,appPackageName, j); } |
1.4 测试结果收集功能
每次运行一个用例结束的时候,会把当前用例的运行结果加入到xml的节点中,所有用例运行结束后,xml文件会保存在PC端的指定位置下。
收集到的测试结果样式如下:
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite name="com.netease.mobile.autotest.testing.LoginTest"> <testcase classname="com.netease.mobile.autotest.testing.LoginTest" name="testUnLogin" time="59.499"/> </testsuite> <testsuite name="com.netease.mobile.autotest.testing.SubscribeTest"> <testcase classname="com.netease.mobile.autotest.testing.SubscribeTest" name="testAdd2" time="74.099"> <failure message="junit.framework.AssertionFailedError: Button with the text: 取消订阅 is not found!" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: Button with the text: 取消订阅 is not found! at com.jayway.android.robotium.solo.Clicker.clickOn(Clicker.java:324) at com.jayway.android.robotium.solo.Solo.clickOnButton(Solo.java:684) at com.netease.mobile.autotest.common.PrisOperation.addSubscribe(PrisOperation.java:91) at com.netease.mobile.autotest.testing.SubscribeTest.testAdd2(SubscribeTest.java:126)</failure> </testcase> </testsuite> </testsuites> |
1.5 测试报告生成功能
首先会获取到收集的测试结果xml文件,然后解析xml同时加入一些css样式,生成html文件,邮件中发送的正文内容就是html文件内容。
这里就是一个解析xml以及样式处理的过程。