Activity的测试非常依赖于Android的Instrumation 框架,和Android其他组件不同的是,Activity具有复杂的生命周期回调函数(如onCreate, onStart 等) ,通常情况下除通过Instrumation 接口外不能直接调用这些回调函数。
- 测试Activity的基本测试类为InstrumentationTestCase,它提供了Instrumentation接口给TestCase的子类。 为了支持Activity测试,InstrumentationTestCase提供了下面功能:
- 生命周期控制: 使用Instrumentation,你可以启动,暂停,中止被测试的Activity。
- Dependency Injection : Instrumentation允许创建一些Mock对象如Context,Application来帮助测试Activity,从而帮助你控制测试环境并和实际的应用的其他部分隔离开来。你也可以定制一些Intent以启动Activity。
- 用户界面交互: 你可以使用Instrumentation向UI发送按键和触摸事件。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="hb.learn.junit"
android:versionCode="1"
android:versionName="1.0">
<!-- 确保minSdkVersion的属性是8或者更高 -->
<uses-sdk android:minSdkVersion="8" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<!-- 在本应用中导入需要使用的包,放在application里面activity外面 -->
<uses-library android:name="android.test.runner" />
</application>
<!-- 记住这个一要放在application外面,不然会出现配置错误信息,targetPackage是被测试的的程序包名 -->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="hb.learn.junit" android:label="Tests for My App" />
</manifest>
UI测试目标
首先初始化测试程序环境
@Overrideprotected void setUp() throws Exception {super.setUp();setActivityInitialTouchMode(true);mainActivity = getActivity();btn = (Button)mainActivity.findViewById(R.id.button1);tv = (TextView) mainActivity.findViewById(R.id.textView1);intent = new Intent(getInstrumentation().getTargetContext(),MainActivity.class);}
public void testPreconditions() {assertNotNull("mainActivity is null", mainActivity);assertNotNull("btn is null", btn);assertNotNull("tv is null", tv);}
例一:检验Activity启动时Button是按照正确布局显示的。
@MediumTest
public void testClickMeButton_layout() {
final View decorView = mClickFunActivity.getWindow().getDecorView();
ViewAsserts.assertOnScreen(decorView, mClickMeButton);
final ViewGroup.LayoutParams layoutParams = mClickMeButton.getLayoutParams();
assertNotNull(layoutParams);
assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
}
在调用assertOnScreen())方法时,你传递根视图以及你期望呈现在屏幕上的视图作为参数。如果你想呈现的视图没有在根视图中,该方法会抛出一个AssertionFailedError异常,除非测试通过。
你也可以通过获取一个ViewGroup.LayoutParams对象的引用验证Button布局是否正确,然后调用声明方法验证Button对象的宽高属性值是否与预期值一致。
例二:检验TextView的布局参数
@MediumTest
public void testInfoTextView_layout() {
final View decorView = mClickFunActivity.getWindow().getDecorView();
ViewAsserts.assertOnScreen(decorView, mInfoTextView);
assertTrue(View.GONE == mInfoTextView.getVisibility());
}
@MediumTest
public void testClickMeButton_clickButtonAndExpectInfoText() {
String expectedInfoText = mClickFunActivity.getString(R.string.info_text);
TouchUtils.clickView(this, mClickMeButton);
assertTrue(View.VISIBLE == mInfoTextView.getVisibility());
assertEquals(expectedInfoText, mInfoTextView.getText());
}
注意
:
Touchutils
助手类提供与应用程序交互的方法可以方便进行模拟触摸操作。你可以使用这些方法来模拟点击,轻敲,或应用程序屏幕拖动View。
警告Touchutils方法的目的是将事件安全地从测试线程发送到UI线程。你不应该用Touchutils直接在UI线程或任何标注@UIThread的测试方法这样做可能会增加错误线程异常。
注释介绍:通常情况下,只需要几毫秒的时间的应该被标记为@SmallTest,长时间运行的测试(100毫秒或更多)通常被标记为@MediumTest,@LargeTest,主要取决于测试访问资源在网络上或在本地系统。
一个Activity测试单元是快速验证一个Activity的状态以及与其它独立组件(也就是和系统其它部分分离的部分)交互的最优方式。一个测试单元通常是测试代码中可能性最小的代码块(可以是一个方法,类,或者组件),而且也不依赖于系统或网络资源。比如说,你可以写一个测试单元去检查Activity是否有正确的布局或者它的触发器,以及Intent对象的正确性。
测试单元一般不适合测试与系统有复杂交互事件的UI。相反,你应该使用像ActivityInstrumentationTestCase2的类作为测试UI组建的描述。
测试目标:
-
验证当Button被按下是启动的LaunchActivity是否正确。
-
验证启动的Intent是否包含有效的数据。
继承ActiviUnitTestCase的Activity不会被Android自动启动的。要单独启动Activity,你需要显式的调用startActivity()方法,并传递一个Intent来启动你的目标Activity。
public class LaunchActivityTest extends ActivityUnitTestCase<LaunchActivity> {
...
@Override
protected void setUp() throws Exception {
super.setUp();
mLaunchIntent = new Intent(getInstrumentation()
.getTargetContext(), LaunchActivity.class);
startActivity(mLaunchIntent, null, null);
final Button launchNextButton =(Button) getActivity().findViewById(R.id.launch_next_activity_button);
}
}
例一:验证一个activity的启动
@MediumTest
public void testNextActivityWasLaunchedWithIntent() {
launchNextButton.performClick();
final Intent launchIntent = getStartedActivityIntent();
assertNotNull("Intent was null", launchIntent);
final String payload = launchIntent.getStringExtra(NextActivity.EXTRAS_PAYLOAD_KEY);
assertEquals("Payload is empty", "hello world", payload);
}
public static final String EXTRAS_PAYLOAD_KEY="hello";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void onShowActivity(View v) {Intent intent = new Intent(this,SecondActivity.class);intent.putExtra(EXTRAS_PAYLOAD_KEY, "hello world");startActivity(intent);}