Android单元测试初探——Instrumentation

鉴客 发布于 2011/09/05 16:39
阅读 85K+
收藏 36

学习Android有一段时间了,虽然前段时间对软件测试有了一些了解,不过接触android的单元测试却是头一次。这几天在物流大赛上也用了不少时间,所以对于android的单元测试没有太深入的研究,所以先写个基本入门吧!

首先,我们来了解一下android的测试类的层次结构:

可以看出android中的测试方法主要有AndroidTextCase和InstrumentationTextCase。在这篇文章中,我将介绍Instrumentation这种测试方法,那么什么是Instrumentation?

Instrumentation和Activity有点类似,只不过Activity是需要一个界面的,而Instrumentation并不是这样的,我们可以将它理解为一种没有图形界面的,具有启动能力的,用于监控其他类(用Target Package声明)的工具类。

下面通过一个简单的例子来讲解Instrumentation的基本测试方法。

1.首先建立一个Android project,类名为Sample,代码如下:

package com.hustophone.sample;
 
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
 
public class Sample extends Activity {
    private TextView myText = null;
    private Button button = null;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myText = (TextView) findViewById(R.id.text1);
        button = (Button) findViewById(R.id.button1);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                myText.setText("Hello Android");
            }
        });
    }
 
    public int add(int i, int j) {
        return (i + j);
    }
}

这个程序的功能比较简单,点击按钮之后,TextView的内容由Hello变为Hello Android.同时,在这个类中,我还写了一个简单的add方法,没有被调用,仅供测试而已。

2. 在src文件夹中添加一个测试包,在测试包中添加一个测试类SampleTest

测试类的代码如下:

package com.hustophone.sample.test;
 
import com.hustophone.sample.R;
import com.hustophone.sample.Sample;
import android.content.Intent;
import android.os.SystemClock;
import android.test.InstrumentationTestCase;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
 
public class SampleTest extends InstrumentationTestCase {
    private Sample sample = null;
    private Button button = null;
    private TextView text = null;
 
    /*
     * 初始设置
     * @see junit.framework.TestCase#setUp()
     */
    @Override
    protected void setUp()  {
        try {
            super.setUp();
        } catch (Exception e) {
            e.printStackTrace();
        }
        Intent intent = new Intent();
        intent.setClassName("com.hustophone.sample", Sample.class.getName());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        sample = (Sample) getInstrumentation().startActivitySync(intent);
        text = (TextView) sample.findViewById(R.id.text1);
        button = (Button) sample.findViewById(R.id.button1);
    }
 
    /*
     * 垃圾清理与资源回收
     * @see android.test.InstrumentationTestCase#tearDown()
     */
    @Override
    protected void tearDown()  {
        sample.finish();
        try {
            super.tearDown();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
    /*
     * 活动功能测试
     */
    public void testActivity() throws Exception {
        Log.v("testActivity", "test the Activity");
        SystemClock.sleep(1500);
        getInstrumentation().runOnMainSync(new PerformClick(button));
        SystemClock.sleep(3000);
        assertEquals("Hello Android", text.getText().toString());
    }
 
    /*
     * 模拟按钮点击的接口
     */
    private class PerformClick implements Runnable {
        Button btn;
        public PerformClick(Button button) {
            btn = button;
        }
 
        public void run() {
            btn.performClick();
        }
    }
 
    /*
     * 测试类中的方法
     */
    public void testAdd() throws Exception{
        String tag = "testAdd";
        Log.v(tag, "test the method");
        int test = sample.add(1, 1);
        assertEquals(2, test);
    }
}

下面来简单讲解一下代码:
setUp()和tearDown()都是受保护的方法,通过继承可以覆写这些方法。

在android Developer中有如下的解释
protected void setUp ()
Since: API Level 3
Sets up the fixture, for example, open a network connection. This method is called before a test is executed.

protected void tearDown ()
Since: API Level 3
Make sure all resources are cleaned up and garbage collected before moving on to the next test. Subclasses that override this method should make sure they call super.tearDown() at the end of the overriding method.

setUp ()用来初始设置,如启动一个Activity,初始化资源等。
tearDown ()则用来垃圾清理与资源回收。

在testActivity()这个测试方法中,我模拟了一个按钮点击事件,然后来判断程序是否按照预期的执行。在这里PerformClick这个方法继承了Runnable接口,通过新的线程来执行模拟事件,之所以这么做,是因为如果直接在UI线程中运行可能会阻滞UI线程。

2.要想正确地执行测试,还需要修改AndroidManifest.xml这个文件.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.hustophone.sample" android:versionCode="1"
    android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <!--用于引入测试库-->
        <uses-library android:name="android.test.runner" />
        <activity android:name=".Sample" android:label="@string/app_name">
           <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>
    </application>
 
    <uses-sdk android:minSdkVersion="3" />
    <!--表示被测试的目标包与instrumentation的名称。-->
    <instrumentation android:targetPackage="com.hustophone.sample" android:name="android.test.InstrumentationTestRunner" />
</manifest>

经过以上步骤,下面可以开始测试了。测试方法也有以下几种,下面介绍两个常用的方法:

(1) 用Eclipse集成的JUnit工具
在Eclipse中选择工程Sample,单击右键,在Run as子菜单选项中选择Android JUnit Test

同时可以通过LogCat工具查看信息

(2) 通过模拟器运行单元测试

点击模拟器界面的Dev Tools菜单

再点击Instrumentation选项,进入Instrumentation菜单

这里有一个InstrumentationTestRunner,它是测试的入口,点击这个选项,就可以自动运行我们的测试代码。以下为运行结果:

按钮点击前

按钮点击后

至此,一个简单的测试过程结束了。当然,android的测试内容还有很多,也有比较复杂的,我会在以后的学习过程中继续分享我的体会。好了,今天就到这里吧!

加载中
0
王东泽
王东泽
楼主,有人问题想问下。这样做单元测试的好处是神马呢?   就是简化错左吗。易于查找错误
l
libz
下功夫做单元测试,就会有许多测试用例,用来验证程序是否实现了预定的功能;通过楼主提供的方式,把每个测试用例尽可能的通过代码来实现,实现执行过程,可以很方便的执行测试过程
0
xianming
xianming
我想也知道,这样测试的好处,检测内存?还是?
0
SamZel
SamZel
呵呵,,测试一定比不测好。。。的
0
TracyZhang
TracyZhang
测试能再做早的时候发现潜在的错误
0
泽东我是少奇
泽东我是少奇
请问测试类中的测试方法是什么时候执行的呢?
0
李支锋
李支锋

误人子弟,测试程序本来就是错的 privateSample sample =null;会报空指针错误的!

l
libz
我从这个例子中收获很大,谢谢作者发表这篇文章,因此为引子,可以收获更大
qiaoning13256
qiaoning13256
不会,在setUp方法中获得了它的实例,这句: sample = (Sample) getInstrumentation().startActivitySync(intent);
0
w
wllchandle
PerformClick 继承Runnable ,怎么会开新线程
yeluosuifeng2005
yeluosuifeng2005
回复 @foool : runOnMainSync这个方法是说将run在主线程(UI线程)中去执行,并不是新启线程,而且run里的操作才会阻塞主线程
taska
taska
Runnable就是开线程的!
0
l
libz

非常实用的例子,对于理解instrumentation帮助很大,按照这个例子实现了基本的JUnit测试,非常感谢!


0
jsbxh
jsbxh
No instrumentation runner found for the launch, using android.test.InstrumentationTestRunner 这个是什么错误!
返回顶部
顶部