Android/OPhone单元测试指南

2012-12-21  张丽丽 

转至:  http://www.51testing.com/html/32/n-828232-5.html
 
一、什么是单元测试

  单元测试是检查软件设计中最小单元正确性的测试。单元,指的就是一个应用程序中最小的可测部分,在Android/OPhone应用中,我们可以把单元定位为某一个方法。因此,Android/OPhone单元测试就是验证代码中每个方法实现正确与否的测试。

  单元测试属于白盒测试。因为代码的作者是最了解代码结构和内部逻辑的人,因此单元测试最好是由开发人员自己完成。

  二、单元测试的重要性

  单元测试可以帮助开发人员提高代码质量,保证代码实现与设计的一致性,提高代码的健壮性和可测性;

  开展单元测试,能将缺陷扼杀在编码阶段。缺陷发现得越早,修改的时间成本和人力成本就越低。

  功能测试系统测试黑盒测试从UI发起,不能保证遍历到所有的代码实现,会导致很多隐藏问题。单元测试能从源头上最大限度减少此类问题的发生。

  在需求频繁变更导致代码频繁变化时,单元测试可以帮助开发人员避免修改代码引入新的错误

  三、Android/OPhone单元测试框架

  Android/OPhone平台中整合了JUnit 3测试框架和Instrumentation机制。通过它们,可以对OPhone应用进行有效的单元测试。

  四、OPhone单元测试基础

  OPhone单元测试的基本方法与一般的基于JUnit的单元测试类似,这里简单介绍下JUnit框架:

  JUnit框架为Java单元测试提供了如下功能:

  ● 断言 (Assertion), 以在测试之前或之后测试预期值。

  ● 测试固件(fixture),以模拟正在接受测试的代码正常运行所需的环境(包括所有必需的对象)。

  ● 测试套件(suite),以将各个测试用例组合到一起。

  ● 测试运行器(runner),以运行测试并捕获和报告测试是成功还是失败。

  大多数情况下,创建一组单元测试也就是创建从某个JUnit 测试用例类继承的类,并向该类添加新方法以执行各个单元测试。

  在JUnit 框架中,Test Case指的是一个包括若干个测试方法的Java类,但实际上,每个测试方法才是我们一般理解中的测试用例。

  下面是一个标准JUnit单元测试类的实例。

在ExampleTest例子中

  ExampleTest继承自junit.framework.TestCase,setUp()和tearDown()是测试固件,setUp()中设置测试的初始条件,tearDown()负责清除测试环境,保证每个测试用例执行时环境的独立性。

  testReading1() 和testReading2()是实际的测试用例,它们的修饰符必须是public,返回值为void,方法名必须以test为前缀。

  test方法中的assertNotNull()和assertEqual()都是断言语句,是每个测试用例中的结果校验点。

每个测试用例执行的顺序都是:setUp() -> test() -> tearDown()

  在JUnit框架中,可以使用测试套件TestSuite来组织测试用例。下面是一个TestSuite的实例,假设现有ExampleTest1和ExampleTest2两个测试类。

当通过TestRunner运行ExampleTestSuite的时候,包括在ExampleTest1和ExampleTest2中的测试用例都会被执行。

  Android/OPhone平台对标准JUnit进行很多扩展和增强,使之更加适合与对OPhone应用进行测试,也可以实现比标准JUnit单元测试更多的功能。例如,从TestCase继承出多个层次的测试类

对于OPhone单元测试,比较常用的子类是AndroidTestCase、ActivityInstrumentationTestCase2和ActivityUnitTestCase。

  详细的API说明,可参考Android SDK中的文档。

  五、Android/OPhone单元测试实例

  Android/OPhone单元测试开发环境和一般的Android/OPhone应用开发环境相同,搭建方法不再赘述。

  下面以对OPhone SDK自带的Snake应用进行测试说明OPhone单元测试的步骤

  步骤一、导入被测应用。

  ● 在Eclipse的Navigator窗口选择New -> OPhone Project。

  ● 在Contents栏中选择Create project from existing source,并点击Browse按钮,之后在浏览窗口中找到Snake应用的源代码所在文件夹(在OPhone SDK安装目录的platforms/android-1.5/samples/apkophoneapps/Snake目录下),选择确定。

  ● 选择Finish

  步骤二、新建测试工程。(步骤二-步骤四在最新的ADT中可直接通过创新Android Test Project完成)

  ● 在Eclipse的Navigator窗口选择New -> OPhone Project。

  ● 在Contents栏中选择Create new project in workspace。

  ● Project name填SnakeUnitTest。

  ● Properties栏中Package name填oms.unittest.snake。

  ● Create Activity一项前面的复选框可以不选中。

  ● 最后点击Finish。

步骤三、设置测试工程的编译路径。

  ● 在SnakeUnitTest工程上点击右键,选择Properties。

  ● 在Properties窗口中选择Java Build Path。

  ● 切换到Projects标签,点击Add按钮,然后在工程选择窗口勾选被测应用Snake工程。

  ● 切换到Libraries标签,点击Add Library按钮,在选择窗口选择User Library并点击Next,再点击User Libraries。在User Libraries窗口点击New按钮,填入自定义Library的名字,如OPhone,然后点OK。再选择Add Jars,在弹出窗口切换至OPhone SDK安装目录的platforms/android-1.5目录,选择oms.jar, 最后点OK。

  步骤四、编辑测试工程的AndroidMenifest.xml。

  ● 展开SnakeUnitTest,双击AndroidMenifest.xml 。

  ● 在编辑窗口,将AndroidMenifest.xml 的内容改为如下图所示内容:

● <uses-library android:name=“android.test.runner”></uses-library>必须定义;

  ● android:lable,为可选项,值可以根据项目实际自定义;

  ● android:name,必须定义,是TestRunner的名字,也可自定义新的TestRunner;

  ● android:targetPackage,必须定义,对应被测应用的包名。

  步骤五、创建测试类。

  ● 在oms.unittest.snake包下创建SnakeTest.java。每个测试类对应被测应用的一个类,并命名为“被测类名Test”的样式。如Snake应用有Snake、SnakeView和TileView三个类,对应的测试类分别是SnakeTest、SnakeViewTest和TileViewTest。

  ● SnakeTest.java的框架如下图所示:

在SnakeTest中,包括对com.example.android.snake.Snake中各个方法的测试用例,如testOnPause()就是对Snake中的onPause()方法进行测试。“test被测方法”是推荐的单元测试命名方式,这样有助于提高测试代码的可读性。

  SnakeTest继承自ActivityInstrumentationTestCase2,是因为被测的Snake.java中定义的是Snake这个Activity的onCreate()和onPause()等,使用ActivityInstrumentationTestCase2的子类可以方便的对这些方法进行测试,并且能得到被测应用和测试应用本身的context和resource。当然,也可以使用InstrumentationTestCase的子类,自主控制Activity的启动和关闭。

  下图是TileViewTest的片段:

TileViewTest继承自AndroidTestCase。AndroidTestCase与一般的JUnit TestCase的主要不同之处在于它可以通过getContext()方法得到被测应用的context,但无法得到测试本身的context,也就无法获得测试应用自己的resource。当有Res.getXml(R.xml.test)这种访问resource的需要时,必须修改或调用被测应用的resource。这一点需要注意。

下图是为com.example.android.snake.SnakeView写的SnakeViewTest的片段:

如图所示,SnakeViewTest中有一个test01InitialCondition()方法,在这个测试用例中检查了所有在setUp()中定义的初始条件的正确性。这是推荐的测试方式。因为setUp()中如果有错误,会影响所有测试用例的结果。由于JUnit框架是按照test后面的字母排序决定测试方法的执行顺序,因此示例中使用了test01这样的前缀以使该用例第一个被执行。

  SnakeViewTest中使用了util.setStatusText()和util.getStatusText()方法。这里的util是测试包中TestUtil类的实例。将测试中用到的一些公用方法放到一个类中管理,有助于提高代码的复用度,降低被测应用代码变化后对测试代码的影响。

  下图是TestUtil类的内容:

 对于被测类中的protected和private类型的变量和方法,可以用反射的方法访问,如getStatusText() 和setStatusText()。

  在一般的JUnit单元测试时,会建议测试代码和被测代码的包名保持一致,以便于访问protected类型的变量和方法。OPhone单元测试代码也可以放在与被测代码同名的包中,方案如下:

  1、测试代码放在单独的工程中,该工程的包名与被测代码包名一致;

  2、在被测应用代码下新建目录,如tests/src,在该目录下增加与被测代码使用相同包名的测试代码,并将该目录添加到整个工程的编译路径中。

  但这两种方案都有一个共同的缺点:需要修改被测应用的代码,如被测应用的AndroidMenifest.xml中必须增加uses-liibray和Instrumentation信息。当需要特殊的测试资源时,方案2还必须修改被测应用的resource资源,并不能得到单独的测试apk。

综合考虑,建议采用前述的独立测试工程+独立包名的方式,并使用反射机制访问非public的变量和方法。

  步骤六、创建TestSuite或TestRunner(可选,适用于命令行执行方法)

  建议使用TestSuite以灵活配置测试用例的执行。下面是一种常用的TestSuite。如果运行MyTestSuite,则SnakeTest和TileViewTest中的测试用例将被执行。

也可以使用TestRunner来控制运行哪些测试类。如果定义了TestRunner,则需要在测试代码的AndroidMenifest.xml增加相应的Instrumentation定义。

  下图是TestRunner的示例和相应的Instrumentation信息

步骤七、执行测试。

  执行测试有两种方式:1、通过Eclipse菜单;2、通过命令行。

  首先介绍通过Eclipse菜单的方式。如果运行测试工程中所有的测试用例,可以在工程上点击右键,选择Run As -> OPhone JUnit Test。

  如果某个测试类,可以展开工程上,在该类上点击右键,选择Run As -> OPhone JUnit Test。

  如果单个测试用例(test方法),可以切换到某个测试类的Outline标签,选中一个test方法,点击右键,选择Run As -> OPhone JUnit Test。

  也可以使用命令行来执行测试用例。方法如下:

  假设SDK的路径已经加入到系统环境变量中,已创建名称为test的avd ,被测应用编译生成为Snake.apk,测试代码编译生成为SnakeUnitTest.apk,且两个apk保存在D盘。

  对于Windows系统,打开cmd窗口,执行emulator –avd test启动模拟器。

  执行adb install D:\Snake.apk和adb install D:\SnakeUnitTest.apk安装被测应用和测试包。如遇安装失败,可以先删掉模拟器里已经存在的应用,命令为adb uninstall <pakagename>。

  执行测试命令。

  ● 运行所有测试:

  adb shell am instrument -w oms.unittest.snake /android.test.InstrumentationTestRunner

  ● 运行某个TestSuite或单个测试类:

  adb shell am instrument -e class oms.unittest.snake.MyTestSuite -w oms.unittest.snake/android.test.InstrumentationTestRunner

  或 adb shell am instrument -e class oms.unittest.snake.SnakeViewTest -w oms.unittest.snake/android.test.InstrumentationTestRunner

 

 

381°/3814 人阅读/0 条评论 发表评论

登录 后发表评论