首页 > 移动平台 > 详细

安卓测试基础

时间:2015-10-28 12:26:01      阅读:291      评论:0      收藏:0      [点我收藏+]

android测试框架(Android Testing Framework)是开发环境的一部分,它提供了架构和强大的工具帮助你从单元到框架测试应用的各个方面。


关键特性:

  • 基于 JUnit,可直接使用JUnit测试一些与Android AP不相关的类,或使用 Android的JUint 扩展来测试 Android 组件。如果你刚开始接触 Android 测试,可以先从 AndroidTestCase开始写一些通用目的的测试用例,然后再写较复杂的测试用例。
  • Android JUint扩展提供了对 Android 特定组件的测试类,提供了创建mock对象辅助方法及控制组件生命周期的方法。
  • Test suite包含在测试包中,操作和主程序包类似。
  • Eclipse ADT包含了创建和测试的SDK 工具,并可通过命令行在其它IDE使用。这些工具从被测试的应用读取信息并自动创建测试包的build 文件,mainfest文件和文件目录结构。
  • SDK 提供了moneyrunner(用python书写脚本)及Monkey(发送伪随机事件的UI压力测试工具)。


本文档描述了android测试框架的基础,包含测试结构、开发测试的API以及启动测试和查看测试结果的工具。本文档假设你有android应用编程以及JUnit测试的基础。

测试框架图如下:

技术分享

测试结构

android的构建和测试工具假设测试项目都组织成类似测试、测试类、测试包和测试项目的标准结构。

android测试基于JUnit。通常JUnit测试就是测试“待测试应用”的方法。测试方法构成的类为test cases (或者test suites)。

JUnit中编译测试源文件到class文件中。类似地android中用android的编译工具编译测试包中的测试源文件为class文件。在JUnit 中test runner来执行测试类。在android中使用测试工具加载测试包和被测应用,然后调用android的test runner。

 

测试项目

测试项目就是一个目录或者eclipse 工程,可在其中新建源代码、manifest文件以及测试包的其它文件。建议使用Android tool创建测试项目:

  • 自动配置InstrumentationTestRunner为测试执行器。必须使用InstrumentationTestRunner或者其子类来执行JUnit测试。
  • 为测试包取合适的名字。如果被测应用为com.mydomain.myapp,Android tool自动命名测试包名为com.mydomain.myapp.test。
  • 自动创建合适的构建文件、manifest 文件以及目录结构。


推荐的目录结构:

  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类可对未使用android API的类进行单元测试。TestCase也是AndroidTestCase(测试依赖于android的对象)的基类。AndroidTestCase还提供了android特有的 setup、teardown以及其它帮助方法。

JUnit的Assert类可以显示结果,assert方法比较期望值与实际结果,在比较失败时抛出异常。android同样提供了扩展了比较类型的断言类,以及用来测试UI的断言类。

注意android测试API支持JUnit 3的代码风格,不支持JUnit 4。

Instrumentation

android instrumentation是android系统中一系列控制方法或钩子(hooks)。这些钩子可以的正常生命周期内独立地控制组件。它同样可以控制android如何加载应用。

通常android组件会按照系统指定的生命周期来运行,例如Activity的生命周期开始于它被Intent激活,其onCreate()方法会被调用,接下来是onResume(),当用户启动另外一个应用,onPause()方法会调用 ,如果Activity调用finish()方法,它的onDestroy()方法也会被调用。android framework API不会提供方法让你在代码中直接调用这些回调方法,但是用instrumentation可以。

系统把应用中的所有组件运行在同一个进程中,你可以让一些组件,比如content provider,运行在单独的进程中。但是无法强制应用与另外其他正在运行的应用运行在同一个进程中。

通过android imstrumentation,你可以在测试代码中直接调用回调方法,让你渗透组件的生命周期,就像调试。下面的测试代码演示了如何用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(),它属于android instrumentation API。调用该方法才会启动Activity。你可以提前配置测试所需的环境(test fixture)。

Instrumentation可以加载测试包和被测试应用到同一个进程,这样测试代码可以调用应用组件的方法,修改和检查组件中的域。

Test case 类

android提供了几个继承自TestCase和Assert的test case 类,它们都有andorid 特有的setup、teardown以及其它的辅助方法。


AndroidTestCase


一个通用的test case类,特别是你刚开始学习android测试,可以从AndroidTestCase开始。它继承了TestCase和Assert类。它提供了标准的JUnit中的setup()和teardown()方法,同时还有JUnit的所有Assert方法。另外,它也提供了用来测试权限的方法以及通过清除一定的类引用来防止内存泄露的方法。

组件特有的 test cases

android测试框架的一个重要特点即使组件独有的 test cases 类。这些特有的组件测试需要提供测试前配置、测试后回收的方法控制组件生命周期的方法。同时它们 也提供创建模拟对象的方法。这些类都会在组件特有测试的内容中讲述:

1、Activity Testing

2、Content Provider Testing

3、Service Testing

android并没有为 BroadcastReceiver 提供一个单独的 test case 类。可以通过测试发送Intent对象给它的组件来测试BroadcastReceiver,检查BroadcastReceiver回复是否正确。

ApplicationTestCase

你可以用ApplicationTestCase这个 test case 类来测试Application对象的启动和退出。这些对象维护着应用程序包中所有组件信息的全局状态,这个test case 用于验证manifest 文件中的<application>元素是否正确配置。然而记住这个test case 无法控制应用包组件的测试。

InstrumentationTestCase

如果你想在一个test case 类中使用 instrumentation 的方法,你必须使用InstrumentationTestCase或者它的子类。Activity 的test case 继承该基类,同时扩展了一些辅助Activity 测试的功能。

Assertion 类

因为android test case 类继承自 JUnit,你可以用断言来显示测试结果。assertion 方法将测试返回的真实值和期望值进行比较,如果比较失败它会抛出一个AssertionException。用Assertion比打印log 更方便,而且提供更好的测试性能。

除了JUnit的Assert类的方法,测试API同时也提供了MoreAsserts和ViewAsserts类:

1、MoreAsserts包含更多功能强大的断言,例如进行正则表达式匹配的assertContainsRegex(String, String)。

2、ViewAsserts包含很多关于View的断言,例如它包含用来测试一个View是否在屏幕的特定的(X,Y)位置的assertHasScreenCoordinates(View, View, int, int)方法,这些断言简化了UI中的结合和对准测试。

模拟对象类

为了解决测试过程中的依赖,android提供了用来创建模拟的系统对象的类,比如Context对象、ContentProvider对象、ContentResolver对象以及Service对象。有些 test case也提供模拟的Intent对象。通过使用这些模拟对象,你可以将测试与系统的其余部分隔离开,同时也满足了测试中的依赖,这些类都在包android.test和android.test.mock中。

模拟对象通过不使用或者覆写正常操作来实现将测试与正在运行的系统隔离。例如,MockContentResolver对象用它自有的与系统隔离的本地框架来代替通常的resolver 框架。同时MockContentResolver不使用notifyChange(Uri, ContentObserver, boolean)方法,这样测试环境以外的observer对象不会被异常触发。

模拟对象类通过提供正常类的子类来满足测试的依赖,该子类除了你覆写的方法外其它都是不起作用的。例如,MockResources对象提供了Resources类的一个子类,其中每个方法在调用时都会抛出异常。要使用它,你只需要覆写需要的方法。

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

基本的模拟对象类

MockApplication、MockContext、MockContentProvider、MockCursor,、MockDialogInterface、MockPackageManager和MockResources提供了一种简单有用的模拟策略。它们都是系统中对应的类的“方法不可用”的版本,它们的所有方法在调用时都会抛出UnsupportedOperationException异常。要使用它们,你需要覆写用来满足依赖所需要的方法。

注意:MockContentProvider和MockCursor是API Level 8 中新加入的API。

Resolver 模拟对象

MockContentResolver通过屏蔽系统正常的resolver框架来为content provider 提供隔离的测试。MockContentResolver不是在系统中查找提供authority的content provider,而是使用它自己的内部表,你必须显式地用addProvider(String, ContentProvider)方法将provider添加到表中。

通过这个特性,你可以将一个模拟的content provider与一个authority关联,新建一个provider对象使用测试数据,你甚至可以设置provider的authority为null。事实上,MockContentResolver对象将你的测试与包含真实数据的provider隔离。你可以控制provider的功能,也可以防止测试影响真实数据。

用来测试的Context

android提供了两个Context类来提供测试:

1、IsolatedContext类提供一个隔离的Context,使用该Context的文件、目录和数据库操作都会在一个单独的测试区域。尽管功能有限,该类足以应对系统调用(this Context has enough stub code to respond to system calls)。这个类允许你在不影响当前设备上的真实数据的前提下测试应用的数据操作。

2、RenamingDelegatingContext提供了这样一个Context,它的大部分功能都由一个现存的Context来处理,但是文件和数据库操作都由一个IsolatedContext来处理,隔离的部分使用一个测试目录,并且创建特殊的文件和目录名,你可以自己控制命名,也可以让constructor自动指定。该类为进行数据操作创建一个隔离区域提供了快捷办法,同时不会影响Context其它的正常操作。

运行测试

test case 都是由一个test runner 类来运行,test runner 加载 test case 类、初始化、运行及清理每一项测试。android test runner 必须被注册(must be instrumented),这样启动应用的系统功能可以控制测试包是如何加载test case和被测试包的。你可以通过在测试包的manifest文件中设定一个值来告诉系统使用哪个注册了的test runner。

InstrumentationTestRunner是android中主要的test runner类,它扩展了JUnit test runner框架并且是已经注册的。它能够执行所有由android系统提供的test case 类并且支持所有类型的测试。

你可以指定测试包的manifest文件的<instrumentation>标签内容为Instrumentation 或者它的子类。InstrumentationTestRunner的代码在共享库android.test.runner中,所以它通常没有链接到你的代码,你必须在一个<uses-library>标签中指定它才可以。你不知道自己手动去设定这些标签,带有ADT的eclipse以及android 命令行工具都会自动生成它们并且把它们加到测试包的manifest文件中。

注意:如果你使用的是InstrumentationTestRunner之外的test runner,你必须手动修改<instrumentation>标签并指向你想使用的类。

要运行InstrumentationTestRunner类必须用android 工具调用内部隐藏的系统类。当你用带有ADT的eclipse执行测试的时候,这些类都会被自动调用,当你用命令行工具执行测试的时候,可以用Android Debug Bridge (adb)运行这些类。

系统类加载和启动一个测试包,杀掉被测试应用包正在运行的进程,并且重新加载一个被测试包的实体,然后它们把控制权交给InstrumentationTestRunner,由它来执行测试包中的每个test case。你也可以通过eclipse中的setting或者命令行工具中的flag来控制哪些 test case或者方法在运行。

既不是系统类也不是InstrumentationTestRunner运行被测试包,这是由test case来做的。它要么调用被测试包中的方法,要么调用它自己的可以改变被测试包生命周期的方法。应用程序完全由test case 控制,在一项测试开始前由test case来初始化测试环境,这在前面的测试展示一个Spinner的Activity的code snippet中有演示。

关于更多的运行测试,可以参见 Testing from Eclipse with ADT和 Testing from Other IDEs。

查看测试结果

android 测试框架返回测试结果给启动测试的工具。如果你是在带有ADT的eclipse中运行测试,结果会在一个新的JUnit视图面板中显示,如果你从命令行启动测试,结果会在STDOUT中显示。在任何一种情况下,你都可以看到一份显示每个test case 名字和你所运行的方法的简要总结,你同时会看到所有失败的断言,其中包含指向产生失败的测试代码所在行的链接。失败的断言同时也会列出期望值和实际值。

测试结果根据你所使用的IDE不同而有不同的格式。带有ADT的eclipse的测试结果格式在 Testing from Eclipse with ADT中有描述,从命令行中开始运行的测试结果格式在Testing from Other IDEs部分有描述。

monkey 和 monkeyrunner

SDK提供了两个非常实用的应用测试工具:

UI/Application Exerciser Monkey,通常称为"monkey",它是一个向设备发送伪随机事件流(如击键、触控、手势)的命令行工具。你可以通过Android Debug Bridge (adb)来运行它,对应用程序进行压力测试然后报告遇到的错误。你可以通过每次使用相同的随机数种子来运行它以重复事件流。

monkeyrunner是一套API,也是用Python编写的测试程序的运行环境。该API包含如下功能:连接到一台设备、安装和卸载软件包、截图、比较两张图片(comparing two images)、运行应用程序对应的测试应用。通过该API,你可以写出功能强大复杂的测试。使用该API 的程序可以通过命令行工具monkeyrunner来运行。

处理包名

在整个测试环境中,你需要同时处理android应用包名和java 包标示符。它们都使用同样的命名格式,但是代表着完全不同的实体,为了正确启动测试你需要知道它们之间的区别。

android包名是.apk文件对应的一个独一无二的系统名字,由应用包的manifest文件中<manifest>标签中的"android:package"属性来设定。测试包的名字必须和被测试包的名字不同,通常android工具会用被测试包的名字后加上".test"来作为测试包的名字。

测试包也会用包名来定位它所测试的应用,由测试包的manifest文件中<instrumentation>元素的"android:targetPackage"属性设定。

一个java包标示符对应一个源文件,包名反映了源文件所在目录,它同时会影响类与成员间彼此的可访问性。

创建测试项目的android 工具会帮助你设定一个测试包的名字。根据你的输入,工具会设定测试包的名字以及测试的目标包的名字。只有在被测试应用工程已经存在的情况下这些工具才会起作用。

默认情况下,这些工具会将测试类的包标示符设定为与被测试应用的包标示符一致。如果你想暴露被测试包中的一些成员你可能需要做一些修改。如果要修改,只修改java 包标示符,不要修改android 包名,只修改test case 的源文件而不要修改测试包中R.java文件的包名,因为修改它会造成与被测试包中的R.java类冲突。不要将测试包的android包名修改成和它所测试的应用的包名一样,因为这样它们的名字在系统中不再是独一无二的。

测试什么

What To Test详细地描述了一个android应用中应该被测试的关键功能以及可能会影响该功能的状况。

大部分的单元测试是专门针对你正在测试的andorid组件。Activity Testing、 Content Provider Testing和 Service Testing中都有一章节列出“需要测试什么”。

可以的话,你应该在一台真实的设备上运行这些测试。不行的话,你可以使用Android Emulator来加载已经配置好你所希望测试的硬件、屏幕、版本的android vitual device。

安卓测试基础

原文:http://www.cnblogs.com/pythontesting/p/4916574.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!