返回介绍

测试基础

发布于 2025-03-09 16:39:56 字数 19488 浏览 0 评论 0 收藏 0

测试基础

版本:Android 4.0 r1

原文

http://developer.android.com/guide/topics/testing/testing_android.html

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

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

Android 测试套件是基于 JUnit 的。你可以使用纯的 JUnit 来测试没有调用 Android API 的类,你也可以使用 Android 的 JUnit 扩展来测试 Android 的组件。如果你在 Android 测试方面还是新手,你可以先开始用诸如 AndroidTestCase 这样的通用类,然后在进一步探索其他更复杂的类。

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

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

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

SDK 也提供 monkeyrunner ,一套使用 Python 程序测试设备的 API,还有 UI/Application Exerciser Monkey ,这是一个通过向设备发送伪随机事件从而对 UI 进行压力测试的命令行工具。

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

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

测试结构( 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 的工具来创建测试项目。工具还有这些好处:

自动设置你的测试包使用 InstrumentationTestRunner 作为测试用例的运行器。你必须使用 InstrumentationTestRunner(或者它的子类)来运行 JUnit 测试。

为测试包创建合适的名字。如果待测试应用有一个叫做 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

你可以在普通的 Java 对象上使用 JUnit 的 TestCase 类来做单元测试。TestCase 类也是 AndroidTestCase 类的父类,你可以使用 AndroidTestCase 类来测试依赖 Android 的对象。AndroidTestCase 类除了提供 JUnit 框架中拥有的功能,也提供了专门在 Android 上使用的 setup,teardown 和帮助方法。

你使用 JUnit 中的 Assert 类来显示测试结果。assert 方法将你在测试中期望的结果与实际的结果作比较,并且在比较失败的时候抛出异常。Android 也提供了断言类,它继承了 JUnit Assert 类中的各种断言类型,还提供了另一类用于测试界面的断言。这些内容在 断言类 (Assertion classes)一节中有详细描述。

想了解更多有关 JUnit 的内容,你可以阅读 junit.org 主页上文档。注意 Android 测试 API 仅支持 JUnit 3 的编码风格,不支持 JUnit 4。你也必须使用 Android 的测试运行器 InstrumentationTestRunner 来运行你的测试用类。测试运行器在 运行测试 (Running Tests)一节中探讨。

仪器( 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 的状态

mActivity= getActivity();

// 得到 Spinner 的当前位置

int currentPosition= mActivity.getSpinnerPosition();

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

assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);

这里用到的关键方法是 getActivity() ,它是仪器 API 的一部分。待测试的 Activity 直到该方法被调用的时候才会启动。你可以提前设置一些测试配置,然后再调用该方法来启动 Activity。

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

测试用例类

Android 提供了一些测试用例类,它们都继承自 TestCase 类和 Assert 类,并带有 Android 特定的 setup,teardown 和帮助方法。

AndroidTestCase 类

AndroidTestCase 类是一个有用的通用类,特别是在你刚开始接触 Android 测试的时候。它继承了 TestCase 类和 Assert 类,提供了 JUnit 标准的 setUp() 和 tearDown() 方法,还有所有的 JUnit 里的 Assert 方法。此外,它还提供了测试许可(permission)的方法和通过清除一些类引用来防止内存泄露的方法。

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

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

Activity 测试

Content Provider 测试

Service 测试

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

应用测试用例( ApplicationTestCase )

你使用 ApplicationTestCase 类来测试 Application 对象的 setup 和 teardown。这些对象维护着一个应用包中的所有组件都使用的全局状态信息。这个测试用例在验证清单文件中<application>元素是否被正确设置(setup)时是很有用的。但是注意该测试用例不允许你进行应用包中组件的测试。

仪器测试用例( InstrumentationTestCase )

如果你想在测试用例中用到仪器方法,你必须使用 InstrumentationTestCase 类或它的一个子类。 Activity 测试用例继承了该基类并实现了其他功能帮助 Activity 测试。

断言类( Assertion classes )

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

除了 JUnit 的 Assert 类方法,测试 API 还提供了 MoreAssertsViewAsserts 类:

MoreAsserts 类包含了更强大的断言,比如 assertContainsRegex(String, String) 方法,它能够匹配正则表达式。

ViewAsserts 类包含测试视图时有用的断言。例如 assertHasScreenCoordinates(View, View, int, int) 方法,该方法能测试一个视图在可视屏幕特定的(X,Y) 坐标处是否有位置。这些断言方法简化了对界面几何形状和对齐方式的测试。

模拟对象类( Mock Object classes )

为了在测试中方便地使用依赖注入,Android 提供了创建模拟系统对象的类,比如 Context 对象, ContentProvider 对象, ContentResolver 对象和 Service 对象。一些测试用例还提供了 Intent 模拟对象。你使用这些对象既是为了将测试与系统中的其他测试隔离开来,又能很方便的在测试中使用依赖注入。这些类能在 Java 包 android.testandroid.test.mock 中找到。

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

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

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

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

MockApplicationMockContextMockContentProviderMockCursorMockDialogInterfaceMockPackageManagerMockResources 提供了简单有用的模拟策略。他们是系统中相应对象的空实现(stubbed-out)版本,其中所有的方法在被调用的时候会抛出 UnsupportedOperationException 。使用它们的时候,你重写需要的方法来依赖模拟。

注意MockContentProviderMockCursor 是 API level 8 中新添的。

Resolver 模拟对象 (Resolver mock objects)

MockContentResolver 通过屏蔽掉系统的常规 resolver 框架,提供了对 content providers 的隔离测试。MockContentResolver 使用自己的内部表,而不是提供一个 authority string 去系统内部查找 content provider。你必须显示地使用 addProvider(String, ContentProvider) 方法为内部表增加 provider。

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

测试的上下文( Contexts for testing )

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

IsolatedContext 提供了一个隔离的 上下文 (Context),使用该上下文的文件、目录、数据库操作将发生在测试空间。尽管它的功能受限,该上下文还是有足够的桩代码(stub code)响应系统调用。

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

RenamingDelegatingContext 提供了一个上下文,其中大多数的操作都由一个已存在的上下文处理,但是文件和数据库的操作由一个 IsolatedContext 处理。隔离部分使用测试目录,并创建特殊的文件和目录名。你可以自己控制它们的命名,或者让构造器自动决定。

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

运行测试( Running Tests )

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

InstrumentationTestRunner 是主要的测试运行器。它继承了 JUnit 的测试运行器框架并且装上了测试仪器。它能运行 Android 提供的所有测试用例类,并支持所有可能的测试种类。

你在测试包的清单文件的 instrumentation 元素中指定 InstrumentationTestRunner 或它的子类作为你的测试运行器。InstrumentationTestRunner 的代码在共享库 android.test.runner 中,该库正常情况下没有被连接到 Android 代码中。你必须在 uses-library 元素中指定包含它。你不需要自己手动设置这些元素,Eclipse 的 ADT 工具和 android 命令行工具会在你的测试包清单文件中自动构建他们。

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

你要使用被 Android 工具调用的内部系统类来运行 InstrumentationTestRunner 。当你在装有 ADT 的 Eclipse 中运行测试时,这些类会被自动调用。当你从命令行中运行测试时,你通过 Android Debug Bridge(adb) 工具运行它们。

这些系统类加载和启动测试包时,先杀死所有正在运行的待测应用的进程,然后加载待测应用的新进程。然后它们将控制交给 InstrumentationTestRunner ,由它来运行测试包下的每个测试用例。你还可以使用 Eclipse ADT 插件中的设置选项(settings)和命令行工具的标志位(flag)来控制运行哪些测试用例和方法。

待测应用既不是由系统类启动,也不是由 InstrumentationTestRunner 启动,而是由测试用例直接启动的。测试用例既可以调用待测应用的方法,又可以调用自己的方法,这些方法能在待测应用中触发生命周期时间的事件。待测应用在测试用例的完全控制之下,这就允许测试用例在运行测试之前设置(set up)测试环境(测试 fixture)。这在先前对一个显示 Spinner 控件的 Activity 的测试 代码段 中已经演示了。

想了解更多有关运行测试的内容,请阅读 使用 ADT 在 Eclipse 中测试 或者 在其他 IDE 中测试 的文章。

查看测试结果( Seeing Test Results )

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

测试结果有特点的格式,这与你使用的 IDE 有关。使用 Eclipse ADT 的测试结果格式在 使用 ADT 在 Eclipse 中测试 中描述。使用命令行的测试结果格式在 在其他 IDE 中测试 中描述。

monkey 和 monkeyrunner

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

UI/Application Exerciser Monkey ,通常被叫做“monkey”,是一个向设备发送键盘敲击、触摸、手势等伪随机流的命令行工具。你使用 Android Debug Bridge (adb) 来启动它。你使用它来对你的程序作压力测试并报道遇到的错误。你可以通过每次使用相同的随机数种子启动该工具来重复一个事件流。

monkeyrunner 工具是使用 Python 来测试程序的一套 API 和执行环境。这套 API 包括了连接设备、安装和卸载应用包、截屏、比较两幅图片和运行测试应用测试包的功能。有了 monkeyrunner 命令行工具提供的这套 API,你可以写出大型的、强大的、复杂的测试。

使用包名工作( Working With Package names )

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

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

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

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

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

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

测试什么( What to Test )

测试什么 一章讨论了你应该在 Android 应用程序中测试的关键功能和可能影响功能的关键情形。

大多数单元测试都特定在你正在测试的 Android 组件上。 Activity TestingContent Provider TestingService Testing 每一篇都有题为“测试什么”的小节,列出了可能的测试范围。

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

下一步

学习如何在 Eclipse 中运行测试,请参考 使用 ADT 在 Eclipse 中测试 。如果你不使用 Eclipse 工作,请参考 在其他 IDE 中测试

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

Hello, Testing 教程介绍了在 Hello,World 应用上下文中的基本测试概念和步骤。

Activity Testing 教程是 Hello,Testing 教程的一个很好的后续。它指导你在开发一个更加实际的应用时应该如何完成一个复杂的测试场景。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文