上篇我们使用了白盒示例,对Robotium进行了简单介绍。但需要提醒的是,Robotium的官方定位还是黑盒测试。那么,我们还是书归正传,看看如何使用Robotium进行黑盒测试。本节,将解决以下几个问题。
(1)如何编写黑盒测试案例
(2)如何设置为同一进程
(3)如何进行重新签名
在编写测试案例之前,我们先创建一个测试对象。工程名称定义为RobotiumTestObj,launch Activity仍为MainActivity。目录结构如下。
之后,我们创建keyStore。
keyStore文件的password为123456。
key0的password为abcdef。
用该keyStore生成对应的签名APK,并安装。
(1)在创建测试类
我们在com.breakloop.robotiumdiary下,创建day2的package,并添加名为BlackBoxTest的类。
接下来是跟白盒测试不同的地方。BlackBoxTest要继承自ActivityInstrumentationTestCase2。
public class BlackBoxTest extends ActivityInstrumentationTestCase2{
....
}
(2)指定测试Activity
该步骤也与白盒测试存在差异。在白盒测试中,使用ActivityTestRule进行指定。而黑盒测试,则是依靠ActivityInstrumentationTestCase2构造函数来指定的。
ActivityInstrumentationTestCase2构造时,需要提供一个class(即activity.class)作为传参。我们可以通过class名称,使用反射获取一个class,再将class传入ActivityInstrumentationTestCase2构造函数。
public static Class> forName(String className)
throws ClassNotFoundException
因此,对于黑盒测试,这里仅需要获取apk的launch activity类名即可。
关于如何获取launch activity名称,需要用到android SDK自带工具aapt.exe。这里不再啰嗦,不清楚的童鞋,可以参照博文http://blog.csdn.net/daihuimaozideren/article/details/78267642。
在测试类中添加相关代码
private static final String LAUNCHER_ACTIVITY_FULL_CLASSNAME = "com.breakloop.robotiumtestObj.MainActivity";
public BlackBoxTest() throws ClassNotFoundException {
super(Class.forName(LAUNCHER_ACTIVITY_FULL_CLASSNAME));
}
@Override
public void setUp() throws Exception {
super.setUp();
Solo.Config config=new Solo.Config();
config.commandLogging=true;
config.commandLoggingTag="BlackBoxTest";
solo=new Solo(InstrumentationRegistry.getInstrumentation());
getActivity();
}
@Override
public void tearDown() throws Exception {
super.tearDown();
}
唠叨:
(a) setUp和tearDown类似于JUnit的@Before和After。
(b) getActivity()为ActivityInstrumentationTestCase2的接口方法,用于打开LAUNCHER_ACTIVITY_FULL_CLASSNAME指定的Activity。
(c) getActivity()必须在super.setUp()之后调用。
(d) LAUNCHER_ACTIVITY_FULL_CLASSNAME所指定的Activity,不一定为LAUNCH ACTIVITY。
(3)创建solo实例和测试案例
直接将WhiteBoxTest的相关内容copy,之后做简单修改即可。
public void test1(){
solo.unlockScreen();
solo.sleep(1000);
solo.setActivityOrientation(Solo.LANDSCAPE);
solo.sleep(1000);
solo.setActivityOrientation(Solo.PORTRAIT);
solo.sleep(1000);
}
由于没有使用AndroidJUnit作为Runner,因此测试案例没有使用@Test标注。
ActivityInstrumentationTestCase2的测试案例方法,必须以test开头,区分大小写。
(4)执行测试案例
执行测试案例,结果未如此前白盒测试的效果,报错。日志显示如下,
W/System.err: java.lang.ClassNotFoundException: com.breakloop.robotiumtestobj.MainActivity
为什么呢?
Robotium是对Android Instrumentation的封装,即google的女婿。如果要进行google资源访问的话,需要遵循Instrumentation的规则。
Instrumentation只能对同一进程内,且具有相同签名的资源进行访问。两个条件,缺一不可。
问题明了了。
通常情况下,每个APP都是一个进程(当然,开发时,可以通过设置process id的方式,进行多进程设置)。而每个进程都有一个ID。
如果使用Android Studio开发,你会在主模块的build.gradle中看到application id,即为进程ID。
defaultConfig {
applicationId "com.breakloop.robotiumtestobj"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
通常,它与project的package name相同。
Eclipse开发,则没有application id的概念,可以直接使用package name.
那么,在黑盒的情况下,如何获取进程ID呢?Android Device Monitor可以帮忙。
仅需要将重签名的APK(关于如何重签名,之后会记录)安装,并启动,便能在Monitor中看到进程ID(第一列)。
获取进程ID后,将其设置为测试工程,主模块的application ID即可(build.gradle中)。
似乎万事俱备了!再次在Android Studio中运行BlackBoxTest。
结果,再次报错!
W/System.err: java.lang.ClassNotFoundException: com.breakloop.robotiumtestobj.MainActivity
而且,跟之前是同样的问题。为什么?还是因为进程吗?
查看Console,发现每次运行测试,Android Studio都会安装两个APK,一个app-debug.apk,即测试目标;一个app-debug-AndroidTest.apk。
之所以找不到com.breakloop.robotiumtestobj.MainActivity,是因为安装了错误的测试apk!
即C:\D\proj\robotiumDiary\app\build\outputs\apk\debug\app-debug.apk
而非C:\D\proj\RobotiumTestObj\app\build\outputs\apk\debug\app-debug.apk
每次build,rebuild,Android Studio都会生成自身工程所对应的app-debug.apk,因此,我们需要做的就是让Android Studio对生成的app-dubug.apk进行替换。
替换操作,需要两步。
首先,在测试工程主模块的build.gradle中添加Task,用于完成替换操作。
android {
...
task copyApk(type:Copy){
from "C:\\D\\proj\\RobotiumTestObj\\app\\build\\outputs\\apk\\debug\\app-debug.apk"
into "C:\\D\\proj\\robotiumDiary\\app\\build\\outputs\\apk\\debug"
}
}
其次,要在运行测试案例前,运行上一步的Task。
再次,运行测试案例。
Test running failed: Permission Denial: starting instrumentation ComponentInfo{com.breakloop.robotiumdiary.test/android.support.test.runner.AndroidJUnitRunner} from pid=23849, uid=23849 not allowed because package com.breakloop.robotiumdiary.test does not have a signature matching the target com.breakloop.robotiumdiary
Activity找到了!但signature是下一关BOSS。
每个发布的apk都是具有签名的,即使是Debug版本。Debug版本的keystore文件,位于当前用户的”.android”目录下,文件名为debug.keystore。
注:如果是最新版本的Android Studio,可有在SDK目录下的tools目录找到debug.keystore。跟”.android”目录下内容一致。
签名会在版本更新时用到。如果没有签名,那么任何人都可以对其他人的作品进行更改。签名,即为非对称加密算法的应用。而所有的签名信息,都包含在apk的META-INF目录中。
我们用zip打开一个apk,并进入META-INF目录。
MANIFEST.MF:保存了zip包内(除META-INF目录)所有文件所对应的哈希值。所有哈希值,都成对存在,一行Name(文件相对路径),一行SHA1-Digest(即文件的SHA1哈希值)
CERT.SF:也是一组哈希队列。不同的是,
(a)将MANIFEST.MF也作为了一个计算对象,获取了哈希值,并成对保存了起来。
(b)将MANIFEST.MF中的成对信息,再次计算哈希,跟原有Name配对,再次保存。
CERT.RSA:对CERT.SF的非对称加密。
因此,如果我们要进行重新签名,仅需要将这三个文件从APK中删除,之后使用debug.storekey重签即可。
网上流传两种种常用的签名方法,一种是自动的,一种是手动的。但原理一样。
(1)自动签名
使用Java(TM) Platform SE binary运行re-sign.jar,然后只需要将apk拖入,并指定生成apk的保存目录即可。生成完毕时,会自动弹出package name和launch activity。
re-sign.jar可以从http://troido.de/downloads/category/1下载。上次更新还是在2014年。
需要注意的是,re-sign.jar需要环境变量的支持。
jarsigner是JDK自带的签名工具包,就在JDK bin目录下。
zipalign是Android SDK build tool的工具包,用于将编码进行4字节对齐。但该jar包并不存在于Android SDK的tools目录下!可以从Android SDK build tool目录下某一版本中进行拷贝。
个人在使用re-sign时,吃瘪了。报错ERROR:138。可能还是环境不对,或兼容版本问题。找了许久,就没有合理的解释,于是个人放弃,希望我是个例。
本人用的JDK版本为1.8.0_144,zipalign用的是27.0.3。
当然,自动方式所用软件也不止一种。Robotium Recorder也能够重签名(在用户选择测试APK时,自动生成重签APP,并保存在相同路径下)。
(2)手动签名
其实,re-sign所做的不外乎3件事情,一,删除签名信息。二,使用jarsigner签名。三,使用zipalign对齐。只是re-sign将一切变的简单了。而这些事情,手动一样能够完成。
jarsigner -verbose -digestalg SHA1 -sigalg SHA1withRSA -keystore C:\Users\用户名\.android\debug.keystore -signedjar C:\D\proj\RobotiumTestObj\app\release\app-debug.apk C:\D\proj\RobotiumTestObj\app\release\app-release.apk androiddebugkey
唠叨:
-verbose用于显示详细过程。
-digestalg用于指定哈希算法,即MANIFEST.MF和CERT.SF所用的哈希算法。
-sigalg用于指定CERT.RSA所用的签名算法。
-signedjar用于指定重签名后文件的名称和路径,若不提供,则对原文进行重签名操作。
-keystore用于指定keystore文件。
androiddebugkey为keystore文件中的别名。
附加问题来了?忘了keystore的别名怎么办?
答:使用JDK自带的keytool工具查看keystore内容。不仅能看别名,还能看所支持的签名算法。debug.keystore默认口令为:android
keytool -list -v -keystore C:\Users\user\AppData\Local\Android\sdk\tools\debug.keystore
zipalign -v 4 C:\D\proj\RobotiumTestObj\app\release\app-debug.apk C:\D\proj\RobotiumTestObj\app\build\outputs\apk\debug\app-debug.apk
至此,签名完成!
运行BlackBoxTest,这时,我们期望的画面出现了。
任何apk都可以用上述操作重新签名,但重签后并不代表能够被DEBUG!
下面是正常启动时的日志,如果Debuggable没有开启,程序会一直“Waiting for application to come online”,直至Timeout。因此,在获取apk时,一定要再三确认!珍爱生命~
Launching BlackBoxTest
$ adb push C:\D\proj\robotiumDiary\app\build\outputs\apk\debug\app-debug.apk /data/local/tmp/com.breakloop.robotiumtestobj
$ adb shell pm install -t -r "/data/local/tmp/com.breakloop.robotiumtestobj"
Success
No apk changes detected since last installation, skipping installation of C:\D\proj\robotiumDiary\app\build\outputs\apk\androidTest\debug\app-debug-androidTest.apk
$ adb shell am force-stop com.breakloop.robotiumtestobj.test
Running tests
$ adb shell am instrument -w -r -e debug true -e class com.breakloop.robotiumdiary.day2.BlackBoxTest com.breakloop.robotiumtestobj.test/android.support.test.runner.AndroidJUnitRunner
Waiting for application to come online: com.breakloop.robotiumtestobj | com.breakloop.robotiumtestobj.test
Waiting for application to come online: com.breakloop.robotiumtestobj | com.breakloop.robotiumtestobj.test
Connecting to com.breakloop.robotiumtestobj
...
经过了本文的梳理,想要入手的童鞋应该也清楚ROBOTIUM的七寸了。是否继续,做个了断吧~
至此,对于如何保证黑盒测试,应该不在话下了。