Android测试(基础篇)

来源:互联网 发布:贴贴相传网络诈骗曝光 编辑:程序博客网 时间:2024/05/18 00:39

Fundamentals(基础)

 

本文参考CodingMyWorld所译,原文地址:http://www.cnblogs.com/codingmyworld/

 

在我们学习怎样为自己的应用程序创建测试程序之前,首先要了解Android测试的基础。

Android测试框架,是集成开发环境的一部分,它提供了一个架构和强大的工具来帮助我们从单元测试到框架测试,在各个层次上全面的测试我们的应用程序。

测试框架有一下几个关键特征:

  • Android的测试套件是基于JUnit的。我们可以用原始的JUnit去测试一个没用调用Android API的类,用在JUnit上扩展的Android测试套件完成Android组件的测试。如果你是Android测试的新手,你可以从使用简单的测试用例类入手(比如AndroidTestCase),然后再使用复杂的类。
  • Android在JUnit的扩展部分中提供了特定组件的测试用例类。这些测试用例类不仅包含了创建“模拟对象”的方法,还提供了控制组件生命周期的辅助方法。
  • 测试套件包含在与应用程序包相似的测试程序包中,所以你不需要再去学习一系列为创建测试程序而使用的新设计,新技术和新工具(针对有经验的Android开发人员而言,如果你从事过Android应用程序开发,应当首先了解Android应用程序如何开发和Android应用程序结构)。
  • 在Eclipse中使用ADT工具可以建立测试并完成测试工作。ADT工具可以帮助用户自动的获取待测试应用程序的信息,创建相应的测试工程(包括代码文件,清单文件(配置文件)以及相应的包目录结构)。
  • SDK也提供的MonkeyrRnner,一套使用Python程序测试设备的API,UI/ApplicationExerciser Monkey,一个通过向设备发送伪随机事件从而对UI压力测试的命令行工具。

这个文档的主要目的是描述Android测试框架的基础,包括测试程序的架构,用来开发测试程序的API,以及用来运行测试程序和查看查看结果的工具。此文档假定你已经对Android应用程序开发和JUnit测试方法有一些基本的认识。

下图是对测试框架的概述:

 

 

 

1       测试框架

 

Android测试程序是建立在测试工程是使用标准的测试框架,测试用例类,测试包,和测试工程的基础之上。

Android测试基于JUnit。通常,每个JUint测试实质上是一个方法,这个方法声明它是用来测试应用程序的某一部分。我们把这些JUnit测试组织到一个被称作测试用例(或者叫做测试套件)的类里面。每一个JUint测试独立地测试应用程序中单独的模块。每一个“测试用例”类里都一些相互关联的测试方法(“JUint测试”),当然也常常会有一些辅助方法。

在JUnit中,我们把测源代码写在一个类文件里;与之类似,我们通过Android SDK的构建工具,把一个或多个测试代码放在Androi测试包的类文件中。在JUnit里,我们使用测试运行器执行测试用例;在Android中,我们使用测试工具加载测试包和待测试应用程序,然后测试工具执行Android专用的测试运行器完成测试。

 

2       测试项目(Test Projects)

 

测试,像Android应用程序一样,被组织成项目(Project)。

一个测试项目就是一个Eclipse项目,这个项目里我们可以编写测试代码、清单文件(Manifest File),和测试需要用到的其他文件。Google为我们提供的ADT工具可以创建和更新测试项目,由ADT工具完成创建存放源代码的目录、以及测试项目的资源文件和清单文件(ManifestFile)。

我应当总是使用Android工具去创建一个测试项目。使用工具有以下几点优势,:

  • 自动设置InstrumentationTestRunner作为测试包的测试运行器。我们必须使用InstrumentationTestRunner(或它的子类)运行测试用例。
  • 为测试包起一个合适的名字。如果待测试应用程序有的包命为com.mydomain.myapp,则测试项目的包名为com.mydomain.myapp.test。这有助于分辨两者的关系,避免在系统中产生冲突。
  • 自动为测试项目建立合适构建文件、清单文件(manifestfile) 以及目录结构。这有助于我们建立测试项目,不必通过修改构建文件来设置测试项目和待测试项目之间的关联。

我们可以在计算机的任意位置创建测试项目,但是最好的方式是在被测试的项目目录中,新建一个tests/目录,并把代码放在tests/目录下面。tests/目录要与src/目录在同一层中。例如,如果你的应用程序项目的根目录是MyProject,那你目录结构应当像下面这样:

 

3       测试用API(The Test API)

 

Android的测试API是基于JUnit API的,并且在此基础上,开发了instrumentation框架和一些Android专用的测试类。

 

JUnit

 

你可以使用JUnit 的TestCase类去测试一个纯Java对象。TestCase也是AndriodTestCase的基类,AndroidTestCase类是用来测试依赖Android的对象。除了提供JUnit框架的功能外,AndroidTestCase还提供了Andriod系统专用的安装、销毁和一些辅助方法。

你可以使用JUnit的Assert类输出(display)测试结果。Assert的方法比较实际值与期望值,如果两者比较失败则抛出异常。同时android也提供了其他类型的比较方法,和另一类用来测试UI的断言。

我们必须使用Android提供的测试运行器——InstrumentationTestRunner运行我们的测试用例。

 

装配或仪器(Instrumentation)

 

Android的instrumentation是指Android系统中一系列的控制方法或“钩子(Hooks)”,这些“钩子(hooks)”可以独立地控制一个Android组件的生命周期,也就是控制着Android怎样去加载一个应用程序。

通常情况下,一个android组件的生命周期如何运作是由系统决定的。例如,一个Activity对象的生命周,开始于这个Activity被Intent启动,然后onCreate()方法被调用,接着是onResume();当用户启动另一个应用时,onPause()方法被调用;如果Activity调用了finish()方法,onDestroy()方法被调用。这些都工作都是由Android系统完成,开发者不能通过Android框架直接调用这些方法,但是通过Instrumentation可以做到。

与上面类似,Android系统中同一个应用程序的所有组件运行在同一个进程里。你可以通过Instrumentation把某一个或某几个组件放在在其他的进程里运行。但是你不能在一个已经有应用程序运行的进程中强迫运行另一个应用程序。

通过Android的Instrumentation,你可以在测试代码中调用回调函数。它允许你一步一步地运行组件的生命周期,就像在调试应用程序一样。下图的代码展示了如何使用Instrumentation保存和恢复一个Activity的状态。

这里使用的关键方法是 getActivity(),它是Instrumentation API中的一个方法。在测试环境下如果你不调用此方法,Activity是不会自动启动的。你应当事先设置好测试环境,然后调用此方法启动Activity。

Instrumentation能够把测试程序和待测试应用程序同时加载到一个进程中;这样当测试程序与待测试应用程序运行在同一个进程中时,测试程序就可以调用应用程序的组件里的各种方法,修改、测试这些组件的属性值。

 

测试用例类(Test Case Classes)

 

Android提供了多个测试用例类,这些测试用例类继承自TestCase和Assert类,并实现了Andorid特定的setup、teardown和一些辅助方法。

 

Component-specific test cases(特定组件测试用例)

 

Android测试框架的一个关键特征是,它提供了一些组件专用的测试用例类,满足我们对特定组件测试的需求。组件专用测试用例的方法,实现组件安装(setup),销毁(teardown)等生命周期的控制,也提供了设置模拟对象的方法,更详细的内容可以阅读“组件专用测试”章节。

Android没有提供专门的用于测试BroadcastReceiver组件的测试用例类。我们可以通过测试组件发出Intent请求后BroadcaseReceiver是否做出了正确相应的方式去测试BroadcaReceiver组件。

 

ApplicationTestCase

 

ApplicationTestCase测试用例类是用来测试Application对象的安装(setup)和销毁(teardown),Application对象维护着应用程序组件要用到的一些全局变量和状态信息。ApplicationTestCase测试用例类能帮助我们判断Application是否被正确的设置。注意:此测试类不能控制应用程序中组件的测试。

 

InstrumentationTestCase

 

如果你需要在测试用例类里使用Instrumentation的方法,你必须使用InstrumentationTestCase类或者它的子类。Activity测试用例类就是以它为基类进行扩展,实现了协助完成Activity测试的功能。

 

断言类(Assertion Class)

 

因为Android测试用例类继承自JUnit,所以你能使用断言方法(Assertion)输出测试的结果。一个断言方法(Assertion)会判断实际返回值是否与期望值相同,如果不同则抛出异常。使用断言比打印日志更方便,而且能提高测试的效率。

除了JUnit的断言外,Android测试框架还提供了MoreAsserts和ViewAsserts类:

  • MoreAsserts包含了其他许多功能强大的断言,assertContainRegex(String,String)方法,它可以判断字符串与正则表达式是否匹配。
  • ViewAsserts包含了用于测试View的有用的方法。比如,它的assertHasScreenCoordinates(View,Vie,int,int)方法,用它可以测试一个View是否在屏幕的可见区域上的(x,y)位置上。这个断言方法简单的测试UI中的几何尺寸和是否对齐。

 

Mock object classes(模拟对象类)

 

为了在测试中更方便的使用依赖注入,Android提供用来创建“模拟系统对象”的类,比如模拟Context对象,模拟ContentProvider对象,模拟ContentResolver对象,或是模拟Service对象。某些测试用例中可能还会提供模拟Intent对象。我们使用这些模拟对象既可以使测试独立于系统的其他部分,也可以方便的为测试进行依赖注入。这些类可以在java包android.test和android.test.mock下找到。

模拟对象可以屏蔽、覆盖常规操作,使你的测试测试程序与正在运行的系统隔离。例如,一个MockContentResolver对象使用自己的本地的Resolver框架取代常规的resolver框架,也就把MockContentResolver对象隔离在系统的其他部分之外,MockContentResolver也屏蔽了notifyChange(Uri,ContentObserver,Boolean)方法,这样在测试环境之外的Observer对象就不会被意外的触发。

模拟对象类还通过提供常规对象的子类来方便依赖注入,这个子类没有任何实际的功能,除非你重写了父类的方法。例如,MockResources类提供了一个的Resources的子类,它的所有的方法在被调用时都会抛出异常。如果你要使用它,你需重写那些供必须信息的方法。

这里有一些android提供的虚拟对象类:

 

简单的模拟对象类(Simple mock object classes)

 

MockAppplication,MockContext,MockContentProvider,MockCursor,MockDialogInterface,MockPackageManager和MockResourse 提供了一个简单且有用的模拟策略。这些类是它们相关的系统对象类的屏蔽版,如果你调用他们的方法,它们会抛出UnsupportedOperationException异常;如果你要使用这些类的方法,你需要自己去重写这些方法。

 

Resolver mock objects

 

MockContentResolver使用一个测试用的content provider隐藏了系统常规的resolver框架。你不需要在系统去寻找某个content provider的授权口令,取而代之的是你调用addProvider(String,ContentProvider)添加Privider到MockContentProvider自己的内部数据表中。

使用这个特性,你可以将一个content provider和某个授权字符串关联在一起。这样一来你就能够创建一个真实存在的provider的实例,但使用的是测试数据。你甚至可以把一个空的授权字符串关联到provider对象上。实际上,MockContentProvider对象把我们的测试与含有真实数据的Provider隔离开,这样我们能够控制Provider的功能,又不影响真实数据。

 

Contexts for testing

 

Android提供了两个十分有用的测试Context的类:

  • IsolatedContext提供了一个隔离的Context。使用该Context的文件,目录,和数据库操作只在测试区域内起作用。尽管它的功能收到限制的,但是它有足够的代码响应系统调用。

这个类能够让你在测试应用程序的数据操作时,不影响到当前设备的真实数据。

  • RenamingDelegationContext提供了一个Context,其中的大部分方法使用已经存在的Context处理,但是文件和数据库操作仍是由IsolateContext处理。隔离部分使用测试目录,使用指定的文件和目录名。你可以自己控制文件的命名,也可以让构造器自己决定如何命名。

这个对象提供了为数据操作快速设置测试隔离区的方法,同时又确保其他Context功能的正常使用。

 

4       运行测试(Running Tests)

 

测试用例需要一个测试运行器类运行,这个运行器类负责加载、配置、运行和销毁每一个测试用例。Android测试运行器也是必须被装配(instrumented)的,这样用来启动应用程序的系统程序便可以控制测试包如何加载测试用例和待测试应用程序。你可以通过设置测试包的清单文件(manifest file),告诉Android平台使用哪个被装配的测试运行器。

InstrumentationTestRunner是Android的主要测试测试运行器类,它集成自JUnit的测试运行器框架,同时它也是能被装配(instrumented)的。它可以运行任何由Android提供的测试用例,支持所有可能的测试种类。

你在测试包的清单文件(manifest file)的instrument元素中,指定InstrumentationTestRunner类或者一个它的子类。InstrumentationTestRunner的代码在共享函数库android.test.runner中,但是通常没有链接到android代码中。为了将它包含进来,你必须在users-library元素设置。你不必亲自动手去设置这些元素,因为Eclipse的ADT工具会帮你完成测试包的manifest文件的配置工作,把这些元素添加到清单文件(manifest file)中。

注意:如果你不使用InstrumentationTestRunner作为测试运行器,你必须在清单文件(manifestfile)中重新配置<instrumentation>元素,让它指向你想用测试运行器。

你需要用Android工具去调用内部系统类运行InstrumentationTestRunner。当你使用Eclipse的ADT工具时,ADT会自动帮你完成调用内部系统类。

系统类加载并启动你的测试包,然后检测待测试程序是否在运行,如果待测试程序正在运行,系统将杀死运行待测试程序的进程,接着生成此待测试程序的新实例并将其加载到测试进程中。系统将控制权交给InsrumentationTestRunner,由InstrumentationTestRunner运行测试包中的所有测试用例。通过ADT工具你可以指定运行测试包中的哪个测试用例或测试用例的哪个方法。

待测试应用程序,既不是由系统类也不是由InstrumentationTestRunner在操作,而是由用测试用例直接操作。测试用例既调用应用程序的方法,又通过调用自己的方法触发应用程序的生命周期事件。应用程序完全在测试用例的控制之下,测试用例在运行测试之前,会事先配置好测试环境。

 

5       观察测试结果(Seeing Test Result)

 

Android测试框架将把测试结果返回给启动测试的工具,如果你使用Eclipse的ADT工具启动的测试,那测试结果将显示到JUnit视图窗口中。在测试结果摘要里,你可以看到测试用例的名字和哪些测试方法被运行。你还可以看到所有产生失败的断言的信息:包括指向在测试代码中发生错误的所在行号,列出了失败断言的期望值和实际值。

 

6       正确处理包名(Working With Package names)

 

在Android测试环境中,我们需要处理好Android应用程序包名和Java包名;两者虽然使用相同的命名格式,却表示两个不同的实体。我们需要了解两者的区别,以便能正确的设置我的测试程序。

Android包名是一个.apk文件在系统中的唯一标示符,在应用程序包中的清单文件(manifest file)的<manifest>元素的“android:package”属性中设置。测试程序的包名必须与待测试程序的包名不同,在默认情况下,Android工具会自动在待测试程序的包名后面加上“.test”作为测试程序的包名。

测试程序包里的清单文件(manifest file)的<instrumentation>元素的“android:targetPackage”属性,用来设置此测试程序的目标程序。

Java包名指向源文件,这个包名反映的是源文件的路径。同时它也影响到类之间、成员之间的可见性。

辅助我们创建测试项目的工具会帮我们起一个Android测试程序的包名。根据我们的输入,工具会帮我们设置好测试程序的包名和待测试程序的包命。当然前提是待测试程序的项目已经存在(注意这里不是指.apk文件,而是应用程序项目)。

默认情况下,这些工具把测试类的Java包名与测试包的Android包名设置成一样的。如果你想要给它们包可见性,从而在待测应用中暴露出一些成员,你可能需要做些改动。假如你真要这么做了,请你只改动Java的包名,不要改动Android包名,并且只改变测试用例类的源文件。不要改变在你测试包中生成的R.java类的Java包名。不要将测试包的Android包名改成与待测应用的Android包名一样,因为这样的话系统中的Android包名就不是唯一的了。
原创粉丝点击