Android自动化大讲堂28--Instrumentation前世今生之分析
好了,前世今生都说完了,现在来分析一下吧,顺便带个小插曲,(*^__^*) 嘻嘻……
4.1 Instrumentation前世今生之分析
ActivityInstrumentationTestCase2继承结构如图4-18所示。
图4-18 ActivityInstrumentationTestCase2继承结构
ActivityInstrumentationTestCase2允许InstrumentationTestCase.launchActivity 来启动被测试的Activity。而且,ActivityInstrumentationTestCase2还支持在UI线程中运行测试方法,并能注入Intent对象到被测试的Activity中。这样一来,我们就能直接操作被测试的Activity了。
也正因为ActivityInstrumentationTestCase2有如此出众的优点,它就取代了比他早出世的哥哥:ActivityInstrumentationTestCase,成为了Instrumentation测试的基础。
了解了ActivityInstrumentationTestCase2,咱们就知道刚才修改的用意:通过继承ActivityInstrumentationTestCase2来获取并操作被测试的Activity中的控件(这里是TextView),如此一来,就不会再报出”空指针异常”(NullPointerExeption)了。
Instrumentation继承关系如图4-19所示。
图4-19 Instrumentation继承关系
作为执行application Instrumentation代码的基类,Instrumentation将在所有应用程序运行前初始化,可以通过它监测系统与应用程序之间的交互。
大家是否还记得在《Instrumentation的前世》那一节里,我们建立测试项目后,打开其AndroidManifest.xml,发现其包含<Instrumentation>标签,并自动将待测项目的包名”com.xuben.hellobugben”设为其targetPackage,如代码清单4-28所示。
代码清单4-28 AndroidManifest.xml
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xuben.hellobugben.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="10"/>
<Instrumentation
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.xuben.hellobugben" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<uses-library android:name="android.test.runner"/>
</application>
</manifest>
Instrumentation implementation就是通过的AndroidManifest.xml中的<Instrumentation>标签元素来指定要测试的应用程序,这个元素指定被测应用包名(即”com.xuben.hellobugben”)及如何运行测试程序。
Instrumentation可以提供单元测试及功能级的自动化测试,我们可以通过调用Android SDK提供的测试API设计测试用例,我们创建的测试用例将以APK的方式直接安装到设备上,并在设备上控制、运行及验证被测应用。
测试程序(TestApp)与被测程序(TargetApp)处于同一进程的不同线程中(ActivityInstrumentationTestCase2支持在UI线程中运行测试方法),通过Instrumentation进行交互。这样一来,我们仅需在PC端通过命令即可控制Instrumentation执行测试用例,并返回测试结果。
小插曲:启动新线程进行测试
设置属性,如代码清单4-29所示。
代码清单4-29 设置属性
public void setTv1Bold(Booleanbold){
TextPainttp = textview1.getPaint();
tp.setFakeBoldText(bold);
}
public void setTv2Size(Floatsize){
TextPainttp = textview2.getPaint();
tp.setTextSize(size);
}
设置文本如代码清单4-30所示。
代码清单4-30 设置文本
public void setTxt(String txt){
textview1.setText(txt);
textview2.setText(txt);
}
如果直接进行测试,不启动新线程,如代码清单4-31所示。
代码清单4-4 不启动新线程直接测试
public void testSetTxt() {
// xuben:设置控件文本
hellobugben.setTxt(bugben_txt);
// xuben:获取textview1控件文本
String cmpTxt =textview1.getText().toString();
//xuben:将获取文本与设置文本进行对比
assertTrue(cmpTxt.compareToIgnoreCase(bugben_txt)== 0);
}
运行报错,如图4-20所示。
图4-20 运行fail
查看原因,如图4-21所示。
图4-21 线程安全问题
原因如下:android.view.ViewRootImpl$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views。
Google一下,原来android中相关的view和控件不是线程安全的,我们必须单独做处理。
所以,我们需要启动新线程进行处理,具体步骤如下。
在setUp()中创建Handler对象,如代码清单4-32所示。
代码清单4-32 创建Handler对象
@Override
public void setUp() throwsException{
super.setUp();
// xuben:创建属于主线程的handler
handler = new Handler();
}
创建Runnable对象,在Runnable中进行控件文本设置,如代码清单4-33所示。
代码清单4-33 控件文本设置
// xuben:构建Runnable对象,在runnable中设置文本
Runnable runnableTxt = new Runnable(){
@Override
publicvoid run() {
//xuben:设置控件文本
hellobugben.setTxt(bugben_txt);
}
};
在具体测试方法中通过调用runnable对象实现文本设置,如代码清单4-34所示。
代码清单4-34 启动新线程
// xuben:启动新线程进行控件文本设置
new Thread(){
public void run(){
handler.post(runnableTxt);
}
}.start();
重新运行,结果如图4-22所示。
图4-22 运行正常
更多内容,请点击“阅读原文”,参考《深入理解Android自动化测试》一书,谢谢!