此文来自百度文库。
测试基本原理
android测试框架,作为集成开发环境的一部分,提供了一个架构和强大的工具来帮助你从单元到框架来测试应用程序各个层次的各个方面。测试框架有以下特征:
l android测试套件基于JUnit。你可以很容易地使用JUnit,不需要调用Android API既可以测试一个类,或者不需要通过Android的JUnit扩展就可以测试一个一个android组件。如果你初学Android测试,你可以从通用测试用例类(如AndroidTestCase)开始,然后再使用更高级的类。
l Android Junit扩展提供了特定组件的测试用例类。这些类提供了一些辅助方法,通过这些方法可以创建模拟对象和方法,以此帮助你控制组件的生命周期。
l 测试套件包含在测试包里,与主要的应用程序包相类似,这样你既不需要学习一套新的工具和技术来设计和构建测试了。
l Eclipse的ADT里有可用的构建和测试的SDK工具,对于其他IDE也提供了命令行工具。这些工具从要被测试的应用程序项目获取信息,通过这些信息自动生成构建文件,配置文件和测试包的结构目录。
l SDK还提供了monkeyrunner,用Python语言测试设备的API。还有UI/Application Exerciser Monkey,一个对UI进行压力测试的命令行工具,可以给设备发送随机事件。
文档描述了Android测试框架的基本原理,包括测试的结构,用来开发测试的API,和用来运行和查看测试结果的工具。文档假定你已经具有Android应用程序和JUnit测试方法的知识。
下图简要说明了测试框架:
测试结构
Android的构建和测试工具假定测试项目是以标准的测试结构、测试用例类、测试包和测试项目来组织的。
Android测试基于JUnit。通常来说,一个JUnit测试就是一个方法,测试应用程序的一个部分。把测试方法组织到一个叫做测试用例或测试套件test case or test suites里。每个测试多是一个独立的测试模块。每个类是一个一系列相关联的测试的容器,尽管他经常提供一些帮助方法。
在JUnit里,你在一个类文件了构建一个或多个测试源代码。相类似的,在Android中,你使用SDK的构建工具来构建一个或多个测试源代码到一个Android测试包的类文件里。在JUnit中,你使用一个test runner来执行测试类。在Android中,你使用测试工具加载测试包和要测试的应用程序,然后测试工具执行一个特定的Android test runner。
测试项目
测试想android应用程序一样被组织成项目。
一个测试项目是一个目录或者Eclipse项目,在里面你可以为测试包创建源代码,配置文件和其他文件。Android SDK包含了用于Eclipse ADT插件的工具和命令行工具,用来帮助你创建和更新测试项目。这些工具为源代码、资源和配置文件创建目录。命令行工具还创建你需要的Ant构建文件。
你应该使用Android工具来创建测试项目。有以下好处:
l 自动建立测试包,用InstrumentationTestRunner作为test case runner。你必须使用InstrumentationTestRunner和他的子类来运行JUnit测试。
l 为测试包创建一个合适的名字。如果要测试的应用程序有一个名为com.mydomain.myapp包,Android工具就会将测试包名设置为com.mydomain.myapp.test。帮助你识别他们的关系,避免系统内冲突。
l 为测试项目自动创建合适的构建文件、配置文件和目录结构。帮助你构建测试包而不需要修改构建文件和在测试包与要测试的应用程序间设置链接。
你可以在文件系统中的任何位置创建一个测试项目,但最好的方法就是在应用程序的src/目录的同一个根目录下创建一个tests/目录,以他来作为测试项目的更目录。这方便找到与应用程序关联的测试。例如,应用程序项目根目录是MyProject,那么你应该使用以下的目录结构:
MyProject/
AndroidManifest.xml
res/
... (resources for main application)
src/
... (source code for main application) ...
tests/
AndroidManifest.xml
res/
... (resources for tests)
src/
... (source code for tests)
测试API
android测试API基于JUnit API,并且通过instrumentation框架和Android特定的测试类来扩展。
JUnit
你可以用JUnit TestCase类来对一个简单的java对象进行单元测试。TestCase也是AndroidTestCase的基类,你可以使用AndroidTestCase测试依赖于Android的对象。除了提供JUnit框架,AndroidTestCase还提供了Android特定的setup、teardown和辅助方法。
你可以使用JUnit Assert类来显示测试结果。assert方法比较你测试的期望值和实际结果值,如果比较失败则抛出异常。Android也提供了一个断言的类,扩展了比较的类型,还有另一个断言类来测试UI。这些将在Assertion calsses中介绍。
想学习更多关于JUnit的知识,可以阅读junit.org主页。注意,Android测试API支持Junit3代码风格而不支持JUnit4.还有,你必须使用Android的instrumentation测试运行器InstrumentationTestRunner来运行你的测试用例类。这个测试运行器将在Running Tests部分介绍。
Instrumentation
Android instrumentation是一系列的控制方法或Android系统里的“hooks”钩子。这些hooks独立控制地一个Android组件的生命周期。他们也控制Android如何加载应用程序。
通常来说,一个Android组件运行在一个有系统决定的生命周期里。例如,一个activity对象的生命周期在他被一个Intent激活的时候开始。该对象的onCreate()方法被调用,接着调用onResume()。当用户启动另一个应用程序,onPause()方法被调用。如果activity的代码中调用了finish()方法,onDestroy()方法被调用。Android框架API不提供直接调用这些回调方法的途径,但是你可以通过使用instrumentation来达到直接调用回调方法这个目的。
而且,系统让一个应用程序里的所有组件都运行在同一个进程中。你可以允许一些组件如contentProvider运行在一个另外的进程,但是你却不可以强制一个应用程序运行在与一个正在运行的应用程序相同的进程里。
通过Android instrumentation,你可以在测试代码中调用回调方法。这样使得你可以一步一步的运行生周期的每个阶段,就像你调试组件那样。以下代码片段演示如何使用它来测试一个activity保存和回复状态:
// Start the main activity of the application under test
mActivity = getActivity();
// Get a handle to the Activity object's main UI widget, a Spinner
mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01);
// Set the Spinner to a known position
mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION);
// Stop the activity - The onDestroy() method should save the state of the Spinner
mActivity.finish();
// Re-start the Activity - the onResume() method should restore the state of the Spinner
mActivity = getActivity();
// Get the Spinner's current position
int currentPosition = mActivity.getSpinnerPosition();
// Assert that the current position is the same as the starting position
assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
这里用到的关键方法getActivity()是instrumentation API的一部分。要测试的Activity知道你调用该方法才会启动。你可以先进行测试前的准备工作,然后调用该方法启动Activity。
同时,instrumentation可以把一个测试包和将要测试的应用程序加载到相同的进程中。由于应用程序组件和他们的测试在相同的进程中,所以测试可以调用组件中的方法,修改和检查组件的属性。
测试用例类test case classes
Android提供了几个测试用例类,这些类都是通过扩展TestCast类和Assert类的来的,具有android特定的setup、teardown和辅助方法。
AndroidTestCase
AndroidTestCase是一个很有用的开始Android测试的普通测试用例类。它扩展了TestCase和Assert。他提供了JUnit标准的setUp()和tearDown()方法,同时还有所有JUnit的Assert方法。另外,它提供了测试权限的方法,并且提供了一个通过清除特定类引用来防止内存泄漏的方法。
特定组件的测试用例
Android测试框架的一个主要特性就是它的用于特定组件的测试用例类。这些类用于特定的组件测试需要,具有setup和teardown和组件的生命周期控制方法。他们同时提供了设置mock对象的方法。这些类在特定组件测试主题里介绍:
l activity测试
l ContentProvider测试
l Service测试
Android没有为BroadcaseReceiver提供一个单独的测试用例类。取而代之的是,通过测试给他发送Intent对象的组件,验证BroadcaseReceiver是否正确响应,以此来测试BroadcaseReceiver。
ApplicationTestCase
你可以用ApplicationTestCase测试应用程序对象的setup和teardown。这些对象保留信息的全局状态,这些信息是应该到应用程序包里的所有组件的。这个测试用例在验证配置文件的<application>元素是否正确设置是非常有用。但是注意:该测试用例不允许你控制应用程序里组件的测试。
InstrumentationTestCase
如果你想在一个测试用例类里使用instrumentation方法,你必须使用InstrumentationTestCase或者它的子类。Activity测试用例扩展了这个基类,增加了其他在activity测试中的辅助功能。
Assertion类
因为Android测试用例类是基于JUnit扩展的,所以你可以使用断言方法来显示测试结果。断言方法将返回的实际值与期望值比较,如果比较失败则抛出断言异常。使用断言比使用日志更方便,并且有更好的测试性能。
除了JUnit Assert类方法外,测试API还提供了MoreAsserts和ViewAsserts类:
MoreAsserts包含了更为强大的断言,如assertContainRegex(String, String),用来进行正则表达式匹配。
ViewAsserts包含有关Views的一些有用的断言。例如,包含了assertHasScreenCoordinates(View, View, int, int),用来测试一个View在可见的屏幕上是否具有特定的X和Y坐标位置。这些断言简化了UI的几何图形和定位测试。
Mock(虚拟)对象类
为了使测试中便于进行依赖注入,Android提供了一些类来创建虚拟系统对象,如Context对象、ContentProvider对象、ContentResolver对象和Service对象等。一些测试用例还提供了虚拟Intent对象。使用这些虚拟对象一方面可以不依赖系统进行独立测试,另一方面在测试中便于依赖注入。这些类可以在java包android.test和android.test.mock中找到。
虚拟对象类还便于依赖注入,通过提供一个非功能性的一般对象的子类,除了重写定义。例如,MockResources对象提供了Resources的一个子类,它里边的所有方法在调用的时候都抛出异常。要使用它,你只需要重写那些必须提供信息的方法。
以下这些是Android中可用的虚拟对象类:
Simple mock object类
MockApplication、MockContext、MockContentProvider、MockCursor、MockDialogInterface、MockPackageManager和MockResources提供了一个简单且有用的虚拟策略。他们是响应系统对象类的不完全版本,他们所有的方法被调用时都抛出UnsupportedOperationException异常。要使用它们,你就要重写你需要的方法来提供虚拟依赖关系。
注意:MockContentProvider和MockCursor是API level 8新增的。
Resolver虚拟对象
MockContentResolver通过掩盖正常的系统resolver框架来提供独立的contentProvider测试。MockContentResolver不是用提供的authority字符串在系统中寻找contentProvider,而是使用自己内部特有的表。你必须用addProvider(String, ContentProvider)方法明确的增加providers到这个表中。
基于这个特性,你可以将一个authority与一个虚拟contentProvider关联起来。你可以创建一个真正provider的实例但却能在里面使用测试数据。你甚至可以将一个authority对应的provider设置为null。实际上,MockContentResolver对象把你的测试从包含真是数据的provider中分隔出来。你可以控制provider的功能,并且可以阻止你的测试影响真是的数据。
用于测试的Contexts
Android为测试提供了两个Context类:
l IsolatedContext提供一个独立的Context、文件、目录和数据库操作。虽然他的功能受到限制,但是这个Context有足够的代码片段来响应系统的调用。
l 这个类允许你在不影响真实数据的情况下测试一个应用程序的数据操作。
l RenamingDelegatingContext提供了一个Context,在这个Context当中,大部分的功能有现有的Context来处理,但是文件和数据库操作则由IsolatedContext处理。被分离的部分使用一个测试目录和创建特定的文件和目录名。你可以自己控制命名,或者让构造器constructor自动决定。
这个对象提供了一个快速的方法来建立一个独立的数据操作区域,并且为其他所有的Context操作保留了正常的功能。
运行测试
测试用例由测试运行器类来运行,测试运行器类加载测试用例类、初始化、运行、销毁每个测试。Android测试运行器必须是被instrumented的,这样启动应用程序的系统工具就能控制测试包如何加载测试用例和应用程序。你在测试包的配置文件里告诉android平台用哪个instrumented了的测试运行器。
InstrumentationTestRunner是主要的Android测试运行器。他扩展了JUnit的测试运行器框架而且也是instrumented的。它能运行有Android提供的任何测试用例类,并且支持所有可能的测试类型。
你在测试包的配置文件里的<instrumentation>元素中指定InstrumentationTestRunner或者他的子类。InstrumentationTestRunner代码是属于共享库android.test.runner的,而不是链接到android代码的。要包含他,你必须在<uses-library>元素中指定他。你不需要自己设置这些元素,Eclipse的ADT和android命令行工具会自动构建他们,把他们添加到你测试包里的配置文件中。
注意:如果你使用一个不是InstrumentationTestRunner的测试运行器,你必须修改<instrumentation>元素,指定你想要使用的类。
要运行InstrumentationTestRunner,你要使用有Android工具调用的内置的系统类。当你在Eclipse ADT里运行测试时,这些类被自动调用。当你从命令行运行测试时,你用Android Debug Bridge(adb)来运行这些类。
系统类加载和启动测试包,结束所有正在运行将要测试应用程序的实例进程,并加载一个新的应用程序实例。然后他们把控制权交给InstrumentationTestRunner,让他来运行测试包里的每个测试用例类。你也可以控制哪个测试用例和方法将被运行,通过Eclipse的ADT或者在命令行工具里使用标记。
系统类和InstrumentationTestRunner都不运行要测试的应用程序,而是有测试用例类直接运行应用程序的。它调用应用程序里的方法,或者是调用它自己的方法来触发应用程序的生命周期事件。应用程序在测试用例的完全控制之下,允许测试用例在运行测试前设置测试环境(the test fixture)。在前面的代码片段中已经展示了测试显示Spinner widget的activity。
学习更多有关运行测试的内容,请参看在Eclipse ADT里测试和在其他IDE里测试主题。
查看测试结果
Android测试框架将测试结果返回给启动测试的工具。如果你在Eclipse里用ADT运行测试,结果将被显示在一个新的JUnit view面板。如果你从命令行运行测试,结果将被显示在标准输出。在这两种情况下,你能看见测试摘要,摘要显示每个测试用例的名称和运行的方法。同时可以看见所有失败的断言。并且显示发生断言失败所在的代码行。断言失败也会列出期望值与实际值。
测试结果有一个格式,有你使用的IDE来指定。
monkey和monkeyrunner
SDK提供了两个工具来从功能层面上测试应用程序:
l UI/Application Exerciser Monkey,通常称为“monkey”,是一个命令行工具,向设备发送键盘输入、触摸和手势等随机事件。你可以用adb工具来运行他。用它来应用程序的压力测试并报告发生的错误。你可以通过运行该工具来重复一个相同随机速度的事件流。
l monkeyrunner工具是一个有Python编写的API和测试程序的执行环境。API包含了连接设备、安装和卸载包、截屏、比较两个图片和运行一个测试包等功能。使用这些API,你可以编写一个强大复杂的全面测试。你要用mokeyrunner命令行工具来运行使用这些API的程序。
用包名来工作
在测试环境里,你用到Android应用程序包名和java包标识。两者使用相同的命名格式,但是他们代表着本质不同的实体。你需要知道这些区别才能正确的建立测试。
Android包名是一个唯一的.apk文件系统名,由包配置文件里<manifest>元素的android:package属性来设置。你测试包Android包名必须与要测试应用程序的Android包名不同。默认地,Android工具通过在要测试的应用程序包名后面增加.test来创建测试包名。
测试包使用Android包名来定位要测试的应用程序包。这在测试包配置文件的<instrumentation>元素的android:targetPackage属性中设置。
java包标识应用于源文件。这个包名映射源文件的目录路径。他同时也影响类和方法相互之间的可见性。
Andorid工具为你的测试项目创建一个Android测试包名。工具从你的输入建立测试包名和目标应用程序包名。要使工具得以工作,应用程序项目必须是已经存在的。
默认地,这些工具为测试类建立与Android包标识相同的Java包标识。如果你想应用程序的成员,是的他们包可见,可能会想要改变包名。如果你这样做,只需要改变java包标识不要去改变android包名,并且只需要改变测试用例的源文件。不要改变测试包里自动产生的R.java类的java包名,因为会于应用程序中的R.java类发生冲突。不要将测试包的Android包名改为与应用程序的Android包名一样,因为这样他们的名字在系统里就不唯一了。