Facebook Twitter LinkedIn E-mail
magnify
Home 2011 八月

Android测试教程(12):ServiceTestCase示例

ServiceTestCase 为测试Service提供了一个可控的测试环境,它提供对Service 生命周期的基本支持,并可以通过注入一些依赖对象来控制测试环境以便测试Service。

ServiceTestCase的类继承如下图所示:

Service Lifecycle 支持, 每个Service运行 都遵循一定的顺序(生命周期方法),ServiceTestCase提供下面方法来支持对Service生命周期方法的测试:

  • 每个测试方法调用之前首先会执行setUp 方法,setUp 的基本实现是取得系统Context ,如果你要重载setUp 的话,注意在第一行加上super.setUp.
  • 在调用startService(Intent) 或bindService(Intent) 之后,ServiceTestCase才会调用Service的onCreate 方法,从而使你有机会在Service启动之前对测试环境做些调整。
  • 当你的测试方法调用startService(Intent) 或bindService(Intent) 之后,ServiceTestCase 调用Service 的onCreate 方法,然后再调用Service相应的 startService(Intent)或 service 的bindService(Intent, ServiceConnection, int)方法。并保存用于tracking 和支持Lifecycle 对应的值。
  • 每个测试方法结束后,调用tearDown 方法,这个方法stop 并destroy 被测试的service. 如果你需要重载tearDown, 注意先调用super.tearDown.

Dependency Injection 每个Service都依赖于运行它的Context 对象和Application 对象,ServiceTestCase 测试框架允许你注入这些对象(修改过,Mocked等)以实现真正的单元测试.

LocalServiceTest 的代码如下:

public class LocalServiceTest
 extends ServiceTestCase<LocalService> {

 public LocalServiceTest() {
 super(LocalService.class);
 }

 @Override
 protected void setUp() throws Exception {
 super.setUp();
 }

 @SmallTest
 public void testPreconditions() {
 }

 /**
 * Test basic startup/shutdown of Service
 */
 @SmallTest
 public void testStartable() {
 Intent startIntent = new Intent();
 startIntent.setClass(getContext(), LocalService.class);
 startService(startIntent);
 }

 /**
 * Test binding to service
 */
 @MediumTest
 public void testBindable() {
 Intent startIntent = new Intent();
 startIntent.setClass(getContext(), LocalService.class);
 IBinder service = bindService(startIntent);
 }

}

testStartable 测试对应的Service能否正常启动。

testBindable 测试对应的Service能否绑定成功

 

Android测试教程(11):ActivityUnitTestCase示例

ActivityUnitTestCase 通常用来测试单独Activity。在启动被测试的Activity之前,你可以Inject一个假的Context或是Application ,使用这个Mock的Context中一个隔离环境中运行被测试的Activity。通常用于Activity的单元测试,而不和Anroid系统进行交互。

ActivityUnitTestCase 的类继承关系如下图:

应该要注意的是,作为单纯的单元测试,被测试的Activity 不运行在一般应用运行的环境中也不和其它Activity产生交互。在这种情况下,不能调用下面的方法,如果调用一般会抛出异常:

createPendingResult(int, Intent, int)
startActivityIfNeeded(Intent, int)
startActivityFromChild(Activity, Intent, int)
startNextMatchingActivity(Intent)
getCallingActivity()
getCallingPackage()
createPendingResult(int, Intent, int)
getTaskId()
isTaskRoot()
moveTaskToBack(boolean)

下面的方法可以调用,但一般不起任何作用,你可以使用getStartedActivityIntent()和getStartedActivityRequest() 来检查参数值。

startActivity(Intent)
startActivityForResult(Intent, int)

下面的方法也可以调用,一般也无效果,可以使用isFinishCalled() 和getFinishedActivityRequest检查传入的参数。

finish()
finishFromChild(Activity)
finishActivity(int)

ForwardingTest 的代码如下:

public class ForwardingTest
 extends ActivityUnitTestCase<Forwarding> {

 private Intent mStartIntent;
 private Button mButton;

 public ForwardingTest() {
 super(Forwarding.class);
 }

 @Override
 protected void setUp() throws Exception {
 super.setUp();

 mStartIntent = new Intent(Intent.ACTION_MAIN);
 }

 @MediumTest
 public void testPreconditions() {
 startActivity(mStartIntent, null, null);
 mButton = (Button) getActivity().findViewById(R.id.go);

 assertNotNull(getActivity());
 assertNotNull(mButton);
 }

 @MediumTest
 public void testSubLaunch() {
 Forwarding activity
 = startActivity(mStartIntent, null, null);
 mButton = (Button) activity.findViewById(R.id.go);

 mButton.performClick();

 assertNotNull(getStartedActivityIntent());
 assertTrue(isFinishCalled());
 }

 @MediumTest
 public void testLifeCycleCreate() {
 Forwarding activity
 = startActivity(mStartIntent, null, null);
 getInstrumentation().callActivityOnStart(activity);
 getInstrumentation().callActivityOnResume(activity);

 getInstrumentation().callActivityOnPause(activity);

 getInstrumentation().callActivityOnStop(activity);
 }

}

在setUp 中,创建了一个Mock Intent 对象mStartIntent 用于测试Activity。

按惯例测试方法testPreconditions 通常作为第一个测试方法,如果该方法Fail的话,表示测试所需的条件不满足,此时其它测试一般也会Fail 。但这个方法并不一定是第一个运行。

testSubLaunch 可以测试当前Activity能否成功启动其它Activity,方法getStartedActivityRequest 返回 当前Activity调用startActivityForResult(Intent, int) 的request code. 方法isFinishCalled 将在当前activity 调用finish()或finishActivity, finishFromChild 后返回true. Forwarding 示例参照Android ApiDemos示例解析(7):App->Activity->Forwarding

testLifeCycleCreate 测试Activity的生命周期回调函数,使用getInstrumentation取的Instrumentation 对象,通过Instrumentation对象可以调用Activity对应的生命周期回调函数来测试Activity的onCreate, onStart,onResume 等方法。

ActivityUnitTestCase 还提供了sendKeys 方法模拟按键事件,可以用来测试UI。

 

Android测试教程(10):ActivityInstrumentationTestCase2示例

ActivityInstrumentationTestCase2 主要用来测试一个或多个Activity的功能测试,使用和最终应用同样的运行环境来测试Activity的功能。可以使用正常系统Context (非Mock)来测试Activity的功能。 并允许你创建一些Mock Intent 用来测试Activity的响应。要注意的是,这种TestCase不允许使用Mock的Context和Application对象测试,也就是说你必须使用和应用程序实际运行的环境来测试。

ActivityInstrumentationTestCase2 的继承关系如下图所示:

它的getActivity() 方法可以取得被测试的Activity的实例对象。

ApiDemosTest 的代码如下:

public class ApiDemosTest
 extends ActivityInstrumentationTestCase2<ApiDemos> {

 public ApiDemosTest() {
 super(ApiDemos.class);
 }


 public void testActivityTestCaseSetUpProperly() {
 assertNotNull("activity should be launched successfully",
 getActivity());
 }
}

测试方法testActivityTestCaseSetUpProperly 使用getActivity() ,主要目的是测试本测试的Activity能否正常启动,测试getActivity() 是否非空。

此外ActivityInstrumentationTestCase2 还提供了两个方法:

  • setActivityInitialTouchMode(boolean) 在启动Activity之前设置TouchMode
  • setActivityIntent(Intent) 可以设置启动Activity的Intent 对象来测试Activity
 

Android测试教程(9):ApplicationTestCase示例

前面介绍了Android测试的一些理论知识,从本篇开始的几篇将结合ApiDemoTest示例来介绍Android测试的实例。在此之前可以参照Android测试教程(3):测试项目 创建ApiDemos->tests 测试项目,本项目测试用来测试ApiDemos,主要目的是介绍Android测试框架的使用方法。

当然要测试ApiDemos,事先要创建好项目ApiDemos。下图显示了创建好ApiDemos->Tests后,ApiDemos->Tests中所含的Java类:

Android测试项目也是一个Android应用项目,其基本使用方法和开发一般的Android应用非常类似,Android测试项目主要是利用Android测试框架编写测试用例来测试对应的Android应用的各个方面。

其中AllTests.java 定义如下:

public class AllTests extends TestSuite {

 public static Test suite() {
 return new TestSuiteBuilder(AllTests.class)
 .includeAllPackagesUnderHere()
 .build();
 }
}

使用TestSuiteBuilder 指明所有该包和其子包中定义的TestCase都为最终TestSuite 的一部分(需要被运行的测试)。

AllTests.java 一般可以不用修改的应用到大部分的测试项目中,如果有特殊需要,可以使用android.test.suitebuilder 的类定义那些Testcase 需要包含到最终的测试包(Test Suite)中.

ApiDemosApplicationTests 测试介绍ApplicationTestCase的基本使用方法。

下图为ApplicationTestCase 的继承关系:

ApplicationTestCase 主要用来测试Application 类,提供了对Application类生命周期方法的基本支持,并可以支持一些dependency injection 以帮助构造测试Application的的环境。

Application Lifecycle 支持, 每个Application运行 都遵循一定的顺序(生命周期方法),ApplicationTestCase提供下面方法来支持对Application生命周期方法的测试:

  • 只有当测试用例调用createApplication()后才会执行Application 的onCreate 方法,从而使得你有机会在调用Application的onCreate方法之前对测试框架做些调整。
  • 当测试用例结束时,测试用例的tearDown 方法会被调用,然后会调用Application 的onDestroy()方法停止和销毁Application.

Dependency Injection 每个Application都依赖于运行它的Context 对象,Android测试框架允许你注入一个Mock的或者孤立的context 对象,以实现真正的单元测试,如果只是想做一般的测试,你的Application方法将被注入全功能的Context对象,你可以使用setContext 来注入你定义的Mock 的Context对象,setContext 必须在createApplication 之前调用。测试框架提供了如MockContext, RenamingDelegatingContext, ContextWrapper 来帮助你构造用于测试Applicaton的Context对象。

本例的代码如下:

public class ApiDemosApplicationTests
 extends ApplicationTestCase<ApiDemosApplication> {

 public ApiDemosApplicationTests() {
 super(ApiDemosApplication.class);
 }

 @Override
 protected void setUp() throws Exception {
 super.setUp();
 }

 @SmallTest
 public void testPreconditions() {
 }

 /**
 * Test basic startup/shutdown of Application
 */
 @MediumTest
 public void testSimpleCreate() {
 createApplication();
 }

}

有几个新知识

1. @SmallTest 和@MediumTest标注

Android测试框架可以使用@SmallTest,@MediumTest和@LargeTest 来标注测试方法,这些分类划分主要是根据测试访问数据的位置,如本地,SD卡,网络,下表为通常划分测试等级的基本方法:

Feature Small Medium Large
Network access No localhost only Yes
Database No Yes Yes
File system access No Yes Yes
Use external systems No Discouraged Yes
Multiple threads No Yes Yes
Sleep statements No Yes Yes
System properties No Yes Yes
Time limit (seconds) 60 300 900+

2. testPreconditions

testPreconditions 测试主要用来运行其它测试方法之前来校验Application 对象的初始化情况,和setUp类似,但和setUp 不同的是,testPreconditions只会被运行一次,而setUp通常在执行每个测试方法之前都会运行一次,一般需把它做为Application第一个测试方法,但由于JUnit使用Reflection 来取得测试方法,因此并不一定能保证testPreconditions一定在其它测试方法之前运行。

testSimpleCreate 为测试Application 的测试方法,调用createApplication 会触发Application 的onCreate 方法,测试结束后,执行tearDown ,然后调用Application 的onDestroy 方法,如果Application 的onCreate, onDestroy 运行抛出异常的话,则本测试方法失败,否则表示测试通过。

ApplicationTestCase 还提供了一个terminateApplication 中止Application,可以测试Application 的onTerminate 方法。

 

Android测试教程(8):测试Service

Android 测试框架也提供对Service测试的支持,基本类为ServiceTestCase,因为Service类通常假定和它是和Client是分开使用的,因此你可以无需使用Instrumentation 来测试Service。

当你设计一个Service时,你应该考虑测试用例中如何检查Service的当前状态,比如你在onCreate ,onStartCommand 中启动一个Service,一般没有一个全局变量来表示Service是否成功,你可能需要自己定义一个全局变量用于测试用例中。

ServiceTestCase 中提供getService() 可以取得当前被测试的Service对象。

ServiceTestCase 为AndroidTestCase的子类,因此可以测试和Permission 相关的功能,并提供Mock的Application 和Context 对象为测试Service提供了一个隔离的测试环境。

后面提供具体例子。

 

Android测试教程(7):测试Content Provider

Content Provider 为不同的应用访问数据提供了统一的接口,本篇介绍Android测试包中用于测试Content Provider 的相关知识。

Android 测试包中用于测试Content Provider的基本类为ProviderTestCase2, 允许你在一个隔离环境下来测试Content Provider。 并提供了一些Mock类如IsolatedContext ,MockContentResover 来辅助测试。

和其它测试一样,对于Content Provider测试也是通过InstrumentationTestRunner 来进行的。

编译测试代码的一般方法是通过派生ProviderTestCase2 (为AndroidTestCase  的子类),因此可以使用JUnit和Android平台相关的方法来测试Content Provider。

可以参见后面的实例来了解如何测试Content Provider。