回 帖 发 新 帖 刷新版面

主题:[原创]Android开发关键技术之旅——Java程序员快速学习通道 书连载



Android开发关键技术之旅——有Java基础者的手机开发快速学习通道



针对有一定手机Java开发经验的读者,可以说本书适合现在广大的程序员。想通过学习Android的开发知识,快速积累Android的开发经验,以最快的时间快速入手,时间,是我们的最大的成本。



当当地址:http://product.dangdang.com/product.aspx?product_id=22781997




回复列表 (共20个回复)

11 楼

9.1.3  Android测试示例
下面我们通过一个简单的Android工程来了解在Android应用开发过程中如何来进行测试工作。
如果想在Android里面做单元测试,有两种方法,一种是Java程序员熟悉和常用的JUnit, 但如果直接使用JUnit的话,需要配置JDK的环境、引用类库和执行器Runner,本质上而言这种方法是做一个Java测试,并不是Android测试,所以,通常只能用于测试一些和Android无关的东西,比如业务逻辑,数据封装,数值计算等等。
真正意义上的Android测试使用的是Android在JUnit基础上提供的配器(Instrumentation)。它会为Android单元测试专门提供配器测试执行器(InstrumentationTestRunner),以启动测试过程,相当于JUnit当中TestRunner的作用。这里可以将Instrumentation理解为一种没有图形界面的,可以启动的,并且可以监控其他类(如Activity类)的工具类。任何想成为Instrumentation的类必须继承android.app.Instrumentation。理解这一点,会帮助我们理解在Android中如何编写测试用例以及进行测试的操作和过程。
在Java中要对某个工程进行测试,通常需要在工程文件中编写测试用例类,并且在此类安装测试要求中编写相应一系列测试方法,但这种方法通常需要进行相应的配置工作,比较复杂,也会影响到正常开发的项目。因此,在Android测试框架中,提供了更好的方式,即通过一个外部的测试工程作为测试程序来运行测试程序。这种方式好处很多,它能够使程序配置更为简单,也能够比较明确的将开发和测试代码分离,从而让测试工作不会直接影响到正常的开发工作。
下面,通过一个来自developer.android.com的教程示例来具体说明这个问题,这个示例可以在其resource中找到(教程Hello, Testing )。
1.    准备被测试工程
作为示例,我们可以直接使用最基本的Hello应用程序:
HelloActivity.java
package cn.myapp;

import android.app.Activity;
import android.os.Bundle;

public class HelloActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
    
    public int add(int a, int b){
        return a + b;
    }    
}
这里需要注意三个地方:
a.    类的包名称cn.myapp;
b.    这个Activity的类名HelloActivity,它会在测试类中进行引用;
c.    额外增加了一个公共方法add(),在测试中可以用到。
还需要修改布局文件main.xml,这里需要为TextView添加一个id(txt_info),因为我们会在后面的测试中对这个控件进行引用:
<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"
    android:id="@+id/txt_info"/>

12 楼


2.创建测试工程
下面,我们就需要为被测工程编写测试用例了。它的具体内容包括创建一个独立的测试工程,并编写测试类和测试方法。具体操作如下:
在菜单中点击File-New-Project..,会出现New Project对话框(图9-3):

[img]http://www.tu265.com/di-2d8b1c8017f827c877c52be59f14812e.png[/img]

13 楼


在“新建工程”对话框的Android类别中,我们可以看到除了常规的Android Project选项外,还有一个Android Test Project选项,也就是说,Android SDK提供了一个模板来帮助建立Android测试工程。
点击“Next”,会出现“New Android Test Project”对话框,我们在这里可以对Android测试工程进行相关的设置(图9-4):

[img]http://www.tu265.com/di-df674ccecb897a8d729b192ea327de0b.png[/img]

14 楼

我们这里的重点配置项目主要包括:
&#61656;    Test Target:测试目标及被测试程序工程,选择Hello;
&#61656;    Build Target:编译目标,选择和被测工程一致;
&#61656;    Package name:cn.myapp.test,即测试程序的子包。
完成后点击“Finish”,就可以看见在Eclipse中新创建的工程TestHello,这里我们通过分析这个工程的结构,可以看到这个测试工程基本上和标准的Android工程文件有一点点差别 (图9-5)。

[img]http://www.tu265.com/di-fa4628b6d333157c8ba3a04bd48d4a60.png[/img]

这些差别在于:
&#61656;    没有Activity类文件,因为测试程序具有自己的启动器和程序入口(在Menifest文件中配置),并且执行的程序也会在后面的测试类中定义;
&#61656;    注意这里的包cn.myapp.test是和被测程序是分离的;
&#61656;    AndroidManifest.xml中的内容是不一样的:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="cn.myapp.test"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
<instrumentation 
android:targetPackage="cn.myapp" android:name="android.test.InstrumentationTestRunner" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">

    <uses-library android:name="android.test.runner" />
    </application>
</manifest>
这里需要注意所使用的包,<instrumentation>标签和<uses-library>库引用,这些都表明这是一个用于测试的Android程序。"android.test.InstrumentationTestRunner"表明了测试程序会通过这里执行器进行启动(而不是JUnit Runner)。

15 楼

3.    创建测试类
有了测试工程之后,我们就可以在此工程中定义测试类了。创建测试类的方法就是在cn.myapp.test包下面创建一个Java类:

[img]http://www.tu265.com/di-203c670772f8da35c55ec0179e255bf1.png[/img]

这里需要注意的地方是:
&#61656;    Package:测试类的包,这里是cn.myapp.test;
&#61656;    Superclass:超类,这里继承了ActivityInstrumentationTestCase2,注意一定是“2”,因为前一步版本已经不被推荐使用;
&#61656;    继承的泛型应该是某个Activity类,因为超类使用的是ActivityInstrumentationTestCase2,它可以用于对Activity进行测试,这里暂时使用默认值,我们会在后面进行修改。
创建测试类后,我们需要进行相应的修改,最后的结果如下:
HelloTest.java
package cn.myapp.test;

import cn.myapp.HelloActivity;
import android.test.ActivityInstrumentationTestCase2;

public class HelloTest extends ActivityInstrumentationTestCase2<HelloActivity> {
    public HelloTest() {
        super("cn.myapp", HelloActivity.class);
    }
}
这里的要点是:
a.    引用了被测试Activity类和测试用例类ActivityInstrumentationTestCase2;
b.    将类泛型定义改为<HelloActivity>,表明这个测试类可以对这个Activity展开测试;
c.    创建并定义了构造函数,注意这里的构造函数继承参数,第一个是被测软件包名,第二个是被测Activity类,两个都应该严格对应。
下面,我们就可以为测试过程编写具体的测试方法了。

16 楼

4.    定义测试方法
通常JUnit测试方法的编写有两类,一种是JUnit方法的重写,如包括setUp(),TearDown(),runTest()等等,它可以用于测试过程的控制,如初始化、预执行、关闭处理等等。另一类当然就是根据业务需求编写的测试方法了。下面是我们在测试用例类中实现和编写的一些测试方法:
HelloTest.java
    private HelloActivity mActivity;
    private TextView mView;
    private String resourceString;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        mActivity = this.getActivity();
        mView = (TextView) mActivity.findViewById(cn.myapp.R.id.txt_info);
        resourceString = mActivity.getString(cn.myapp.R.string.hello);
    }
    
    public void test1() throws Throwable{
         int i=4+8;
         assertEquals(5,i);
    }

    public void test2(){
        assertEquals(5, getActivity().add(2, 3));
    }   
    
    public void test3() {
        assertNotNull(mView);
    }

    public void test4() {
        assertEquals(resourceString,(String)mView.getText());
    }
这里编写的几个测试方式主要是说明几种常用的测试操作,简单说明一下:
a.    setUp()这个方法会在测试执行是调用,可以作为初始化使用;
b.    可以使用getActivity()方法获取当前测试类关联的Activity;
c.    获取了Activity之后,可以使用Activity的方法获取关联Activity的视图和字符串等资源等用于测试;
d.    test1()方法判断一个两个变量是否相等;由于通过ActivityInstrumentationTestCase2继承了JUnit Test Case,所以可以直接使用断言方法如assertEquals(),此外,这里应该会返回一个断言失败;
e.    test2()方法可以调用测试目标Activity的某个公共方法,在这里是add(),这里应该为断言正确;
f.    test3()方法可以判断某个视图是否存在;
g.    test4()方法可以取出视图的内容并进一步判断是否和预先的相同。
这样测试结果会是怎样的呢?让我们运行一下。

17 楼

5.    运行测试
要运行测试,我们可以选择测试用例类,点击工具栏上的运行,在运行方式对话框(图9-7)中选择“Android JUnit Test”项目,就可以执行测试了。

[img]http://www.tu265.com/di-6b810d7e4fd50f41a6d78c3d54824c36.png[/img]

这时,Eclipse会使用InstrumentationRunner执行器来运行这个程序,这一点我们可以通过查看程序工程的执行配置了解到(图9-8):

[img]http://www.tu265.com/di-1f199775a237e1b1d2376e215489e1e7.png[/img]

测试程序执行后,我们会在控制台上看到执行的状态,这里程序也会控制打开模拟器和执行测试目标的运行,在模拟器中也可以看到界面的变化,而当测试完成后,测试程序会关闭被测试程序。
当然,也可以选择菜单上的“Run as – Android Unit Test”来启动测试。

18 楼

6.    分析测试结果
测试开始后,系统会打开JUnit窗口(图9-9)来显示测试过程:
 
[img]http://www.tu265.com/di-6422e0031653a7c8ebac668a1973b02b.png[/img]

图 9-9 JUnit测试结果
这个视图中包含了非常多的有用的信息:
&#61656;    测试执行的时间,包括每个步骤使用的时间;
&#61656;    执行测试方法的数量,错误数量和失败数量(断言为假的);
&#61656;    测试失败的方法和失败原因,这里是AssertionFailedError,显然test1()方法我们设计的就是这样的结果;
通过测试的结果,我们就可以了解到程序是否能够按照我们设计的步骤进行正常的运行,以及数据处理的结果是否符合我们的期望,如果错误或者失败,其原因如何,这些都可以帮助开发者对程序进行改进。改进后可以立刻再次进行测试,直到满足我们设计的要求,这就是一个完整的开发测试的过程。

19 楼

7.    通过命令行执行测试
除了在Eclipse中启动之外,测试也可以通过命令行方式执行(需要先打开模拟器和安装被测程序):
adb shell am instrument -w cn.myapp.test/android.test.InstrumentationTestRunner
也可以在命令行窗口中看到测试结果:

[img]http://www.tu265.com/di-5383133eacd42b83cad43a0e9b83805e.png[/img]

这些信息和我们在JUnit中看到的其实是差不多的。

20 楼

8.    高级测试
除了上面的针对Activity的Android单元测试之外,在Android测试框架中还提供了针对Service和Content Provider等的测试方法。此外,这个框架还提供了相关的模拟对象和测试用例类来进行用户界面(UI)操作和状态管理的测试。这里由于篇幅的限制,不再例举完整的示例代码,而是通过简单的分析其核心代码代码片段帮助理解其基本思路和过程。
比如,我们已经知道如何在测试用例中访问Activity的某些视图的内容,下面的代码显示了在测试用例中可以使用sendKeys()方法向UI发送按键操作事件来模拟用户实际的操作:
 this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
    for (int i = 1; i <= TEST_POSITION; i++) {
      this.sendKeys(KeyEvent.KEYCODE_DPAD_DOWN);
    } // end of for loop

this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
而下面的测试用例代码片段则显示了如何控制Activity的状态切换:
@UiThreadTest
public void testStatePause() {
    Instrumentation mInstr = this.getInstrumentation();

    mActivity.setSpinnerPosition(TEST_STATE_PAUSE_POSITION);
    mActivity.setSpinnerSelection(TEST_STATE_PAUSE_SELECTION);

    mInstr.callActivityOnPause(mActivity);

    mActivity.setSpinnerPosition(0);
    mActivity.setSpinnerSelection("");

    mInstr.callActivityOnResume(mActivity);

    int currentPosition = mActivity.getSpinnerPosition();
    String currentSelection = mActivity.getSpinnerSelection();

    assertEquals(TEST_STATE_PAUSE_POSITION,currentPosition);
    assertEquals(TEST_STATE_PAUSE_SELECTION,currentSelection);

简单分析如下:
a.    使用@UIThreadTest注解告知测试方法可以对UI进程进行测试;
b.    使用getInstrumentation()获取一个配器,这个配器可以用于操控被测Activity的状态;
c.    调用配器的callActivityOnPause()可以使被测试的Activity进入暂停状态;
d.    同理callActivityOnResume()可以是被测Activity从暂停状态恢复;
e.    此测试是验证该用户界面是否能在Avtivity暂停和恢复操作(如界面切换等)中恢复正确的状态。
本节我们讨论了Android开发过程中关于软件测试的一些相关内容,下一节我们将会来了解关于软件优化方面的一些问题。

我来回复

您尚未登录,请登录后再回复。点此登录或注册