天天看点

Android开发指南(39) —— Testing Fundamentals

前言

声明

  欢迎转载,但请保留文章原始出处:) 

测试基础

译者署名:codingmyworld

版本:android 4.0 r1

原文

<a href="http://developer.android.com/guide/topics/testing/testing_android.html">http://developer.android.com/guide/topics/testing/testing_android.html</a>

       android测试框架作为整个开发环境不可分割的一部分,提供了一个架构和一些强大的工具,来帮助你在从单元到框架的各个水平上测试你应用的各个方面。

这个测试框架包含了以下主要特性:

* android的junit扩展提供了特定组件的测试用例类。这些类提供了创建模拟对象的帮助方法,还有帮助你控制相应组件生命周期的方法。

* 测试套件被包含在与主应用程序包类似的测试包中,因此你不需要为设计和构建测试再学习新的工具集和技术。

* 构建和测试的sdk工具在eclipse的adt中已经提供了,如果你使用其他的ide,也能以命令行的形式使用。这些工具从待测应用项目中获取信息,并利用这些信息自动的创建构建文件,清单文件,还有测试包的目录结构。

这篇文档描述了android测试框架的基本原理,包括测试结构,用于开发测试的api,还有你用来运行测试和查看结果的工具。此文档假定你已经掌握了android应用开发的基本知识和使用junit测试的方法。

下面这张图总结了整个测试框架:

Android开发指南(39) —— Testing Fundamentals

测试结构(test structure)

android的构建与测试工具设想测试项目被组织成一个标准的测试结构,及测试用例类、测试包、还有测试项目。

android的测试是基于junit的。junit的测试大体上就是一个方法,由方法中的语句对待测试应用的某一部分进行测试。你将这些测试方法组织在一起,放到一个叫做测试用例(或者测试套件)的类中。每个测试在待测试应用的单个模块中都是独立的。每个测试用例类都是相关测试方法的一个容器,尽管它也提供一些帮助方法。

在junit中,一个或多个测试源文件被构建到class文件中;在android中,你同样使用sdk的构建工具将一个或多个测试源文件构建到android测试包中的class文件里。在junit中,你使用测试运行器来执行测试用例;在android中,你使用测试工具加载测试包和待测应用,然后由工具来执行特定的android测试运行器。

测试项目(test projects)

测试,和android应用程序一样,被组织成项目。

一个测试项目是一个目录或者eclipse工程,你在其中创建源代码、清单文件、还有测试包下的其他文件。android sdk包含了为你创建和更新测试项目的工具,既有在eclipse adt中使用的,又有在命令行中使用的。这些工具为你创建存放源代码和资源文件的目录以及测试包的清单文件。命令行工具也会创建你需要的ant构建文件。

你应该总是使用android的工具来创建测试项目。工具还有这些好处:

* 为测试包创建合适的名字。如果待测试应用有一个叫做com.mydomain.myapp的包名,那么android工具会将测试包命名成com.mydomain.myapp.test。这帮助你辨认它们之间的关系,防止系统内的冲突。

* 自动创建恰当的构建文件、清单文件、和测试项目的目录结构。这帮助你在构建测试包的时候无需手动修改构建文件和设置测试包与待测应用之间的联系。

你可以在文件系统的任何地方创建测试项目,但是最佳的方式是将你的测试项目添加到主应用程序项目中,这样测试项目的根目录tests/就和主应用程序的src/目录在同一个目录层次上了。这帮助你很快的找到与应用相关的测试。例如,如果你的应用程序项目的根目录是myproject,那么你应该使用如下的目录结构:

myproject/

      androidmanifest.xml

      res/

          ... (主应用程序的资源)

      src/

          ... (主应用程序的代码) ...

      tests/

          androidmanifest.xml

          res/

              ... (测试使用的资源)

          src/

              ... (测试使用的代码)

测试api(the testing api)

android测试api基于junit的api,并且扩展了一套仪器框架(instrumentation framework)和一些特定的android测试类。

junit

仪器(instrumentation)

在android系统中,仪器是指一组控制方法或者“钩子”。这些钩子控制着android组件,使其独立于它正常的生命周期。它们也控制着android如何加载应用程序。

正常情况下,android组件运行在由系统决定的生命周期中。比如,一个activity对象的生命周期在它被一个intent激活时开始,它的oncreate()方法被调用,然后是onresume()。当用户启动另一个应用,onpause()方法被调用。如果activity的代码调用了finish()方法,那么ondestroy()方法会被调用。android的框架api不提供让你直接调用这些回调方法的方式,但是你可以使用仪器(instrumentation)来做这些。

同样,系统把一个应用程序中的所有组件运行在同一个进程中。你可以允许一些组件,如content provider,运行在一个独立的进程中,但是你不能强制一个应用程序和一个已经运行的应用程序运行在同一个进程中。

然而,有了android仪器,你可以在测试代码中调用那些回调方法。这让你可以一步一步地遍历运行一个组件的生命周期,仿佛你在调试组件。下面的代码片段演示了如何使用仪器来测试一个activity保存和恢复状态:

   // 开始待测应用的主activity

   mactivity= getactivity();

  // 得到该activity对象一个主界面控件的句柄,一个spinner

   mspinner= (spinner)mactivity.findviewbyid(com.android.example.spinner.r.id.spinner01);

  // 设置spinner的已知未知

   mactivity.setspinnerposition(test_state_destroy_position);

   // 结束activity——ondestroy()方法应该保存spinner的状态

   mactivity.finish();

   // 重新启动activity - onresume()方法应该恢复spinner的状态

  // 得到spinner的当前位置

   int currentposition= mactivity.getspinnerposition();

   // 断言当前位置与启动时的位置相同

    assertequals(test_state_destroy_position, currentposition);

同样,仪器能将测试包和待测应用加载到同一个进程中。既然应用组件与它们的测试程序在同一个进程中,测试程序就能调用组件的方法并且修改和检查组件中的属性了。

测试用例类

androidtestcase类

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

android测试框架的一个关键特性就是它的特定组件测试类。这些类提供了安装(setup)和卸载(teardown)测试所需资源(fixture)的方法和控制组件生命周期的方法,解决了对特定组件进行测试的需求。它们还提供了创建模拟对象的方法。我们在特定组件测试的章节中讨论这些类:

android没有为broadcastreceiver提供一个独立的测试用例类,取而代之的是通过向注册broadcastreceiver的组件发送一个intent对象,判断broadcast receiver能否正确响应来进行测试的。

应用测试用例(applicationtestcase)

仪器测试用例(instrumentationtestcase)

断言类(assertion classes)

由于android测试用例类继承自junit,你可以使用断言方法来显示测试的结果。断言方法的作用是将测试返回的实际结果同你期望的结果作比较,并在比较失败的时候抛出assertionexception。使用断言比记录日志更便利,能提供更好的测试效果。

模拟对象类(mock object classes)

模拟对象通过“屏蔽”(stub out)或重写常规方法将测试同正在运行的系统隔离开。例如,mockcontentresolver使用自己的本地框架替代常规的resolver框架,这与系统的其他部分是隔离的。mockcontentresolver也屏蔽了notifychange(uri, contentobserver, boolean)方法,这样测试环境外的观察者对象就不会被不小心触发了。

下面这些是android中可用的模拟对象:

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

resolver模拟对象(resolver mock objects)

有了这个功能,你能将一个模拟的content provider与一个authority关联起来。你能创建一个真实provider的实例,但是仅使用其中的测试数据。你甚至可以设置一个authority的provider为null。实际上,一个mockcontentresolver对象将你的测试同包含真实数据的provider隔离开来了。你可以控制provider的功能,你可以防止测试影响到真实的数据。

测试的上下文(contexts for testing)

android提供了两个上下文类用于测试:

该类允许你测试应用的数据操作,而且不会影响到可能在设备上存在的真实数据。

该对象提供了一种为数据操作设置隔离区的快速方式,并且保留了对其他上下文操作的常规功能。

运行测试(running tests)

测试用例是通过测试运行器类运行的,测试运行器先加载测试用例类,然后安装(set up)测试资源,运行测试用例,最后卸载(tear down)每个测试的资源。android的测试运行器还必须装上测试仪器(instrumentation),以便启动应用的系统工具能控制测试包(test package)如何加载测试用例和待测应用程序。你通过在测试包的清单文件中设置一个值来告诉android平台使用哪个装好仪器的测试运行器。

注意:如果你要使用别的测试运行器而不是instrumentationtestrunner,你必须改变&lt;instrumentation&gt;元素,使它指向你要用的类。

查看测试结果(seeing test results)

android测试框架将测试结果返回到运行测试的工具上。如果你在eclipse中使用adt来运行测试,那么测试结果显示在一个新的junit视图窗格种。如果你从一个命令行中运行测试,那么结果显示在stdout中。在两种情况下,你都会看到一个测试总结显示每个测试用例的名字和其中运行的方法。你还能看到所有发生的断言失败,它们包括了出错行的指针。断言失败还列出了期望值和实际值。

monkey和monkeyrunner

sdk针对功能级别的测试提供了两个工具:

使用包名工作(working with package names)

在测试环境中,你会同时遇到android应用程序的包名和java的包名。这两个包名的命名格式相同,但它们实质上代表了不同的实体。你需要知道这些不同来帮助你正确地设置你的测试。

android的包名是一个.apk文件独一无二的系统名,它由包内清单文件中的&lt;manifest&gt;元素的"android:package"属性设置。你测试包的android包名必须和待测应用的包名不同。默认情况下,android工具在待测应用包名的后面加上".test"来作为测试包名。

测试包同样使用android包名来找到它所测试的应用程序包。这是在测试包清单文件的&lt;instrumentation&gt;元素的"android:targetpackage"属性上设置的。

java的包名符适用于源文件。这个包名反映了源文件的目录路径。它也影响着类之间、成员之间的可见性。

创建测试项目的android工具为你设置android测试包的包名。工具会根据你的输入来设置测试包的包名和待测应用的包名。要想让这些工具工作,必须已经有应用项目了。

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

测试什么(what to test)

可能的话,你应该在真实设备上运行这些测试。不可能的话,你可以使用针对你所测试的硬件、屏幕、版本配置的android虚拟设备的android模拟器。

下一步

如果你想要一个对android测试的一步一步地介绍,尝试一篇测试教程或者示例测试包:

转载:http://www.cnblogs.com/over140/archive/2011/12/13/2285587.html