使用ThreadingTest进行安卓应用单元测试
1、Android单元测试的编写
Android单元测试有两种方法,首先作为java程序,可以试用JUnit Test进行测试,另外也可使用Android JUnit Test进行单元测试。
1)、JUnit Test进行单元测试
JUnit对Android应用程序进行单元测试需要使用Java命令来启动或者在eclipse里面将启动的Bootstrap Entries改为JRE,但是这种只能测试逻辑代码,因为是是运行在JVM上,而不是Android系统中,所以不能测试Android有关的代码。
使用JUnit测试的代码,需要在函数的之前加@Test,函数必须为public类型,在eclipse中,在类上右击,选择JUnit Test即可进行测试。
2)Android JUnit Test进行单元测试
Android JUnit Test单元测试是一组直接或间接继承自junit.framework.Testcase的类集合,入口是InstrumentationTestRunner。
使用Android JUnit Test测试,也存在两种测试方法,测试驱动与测试代码在同一工程下和测试驱动和测试代码位于不同工程,两者之间并无巨大差异,只是在测试时,前者只安装一个App,后者安装两个App。
如下为一个单元测试的实例:
步骤 1:准备好需要测试的源代码
package com.zeroone.unittest;
import android.os.Bundle; import android.app.Activity; import android.view.Menu; 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 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_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) { try { Thread.sleep(1500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return (i + j); } } |
步骤2:添加测试类,编写测试代码,测试onClick和add函数
package com.zeroone.unittest.test;
import com.zeroone.unittest.R; import com.zeroone.unittest.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;
// 初始设置 @Override protected void setUp() { try { super.setUp(); } catch (Exception e) { e.printStackTrace(); } Intent intent = new Intent(); intent.setClassName("com.zeroone.unittest", 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); }
// 垃圾清理与资源回收 @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(1000); getInstrumentation().runOnMainSync(new PerformClick(button)); SystemClock.sleep(1000); assertEquals("Hello Android", text.getText().toString()); Log.v("testActivity", "test the Activity Over"); }
// 模拟按钮点击的接口 private class PerformClick implements Runnable { Button btn = null; public PerformClick(Button button) { btn = button; }
public void run() { btn.performClick(); } }
// 测试add方法 public void testAdd() throws Exception{ Log.v("testAdd", "test the add method"); int test = sample.add(1, 1); assertEquals(2, test); Log.v("testAdd", "test the add method over"); } } |
步骤3:修改AndroidManifest.xml文件
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.zeroone.unittest" android:versionCode="1" android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <uses-permission android:name="android.permission.INTERNET"></uses-permission> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="android.test.runner" /> <activity android:name="com.zeroone.unittest.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>
<instrumentation android:targetPackage="com.zeroone.unittest" android:name="android.test.InstrumentationTestRunner" android:label="Tests for TT application." /> </manifest> |
注意:绿色背景为添加部分。其中android:label="Tests for TT application."在模拟器中的Dev Tools工具中,设置Instrumentation下显示的名称,在步骤4运行中,点击该标签,也可运行单元测试。在TT Android JUnit Test中也是重要一步,可以设置多个单元,多个名称,每个名称尽量不要重复,以区分不同的单元测试。
步骤4:运行测试类
运行测试类有三种方式,包括:
1、 命令行方式
使用adb命令启动单元测试
2、 eclipse中选择Android JUnit Test运行方式
在eclipse中,右击测试工程,选择run as -> Android JUnit Test
3、 模拟器中,使用Dev Tools
在模拟器中安装该程序后,在Dev Tools工具中,选择Instrumentation下显示的与android:label同名的标签运行单元测试。
2、TT单元测试的编写
要使用TT进行单元测试类的编写,需要将所有的测试类写到一个单独的文件夹下,在进行编译工程时,使用参数-filter将其从编译路径中排除,不对其进行插桩。如下图所示:
被测源码放在src文件夹中,单元测试驱动代码放在test_src文件下。
其他方面,需要在Android单元测试上进行的修改为:
1、引入TT Android jar包,JavaParser-Android.jar
2、将继承Android JUnit Test类换成继承TT Android JUnit Test类,TT Android JUnit Test类中与Android JUnit Test相对应的类为在类名前加TT。例如与InstrumentationTestCase相对应的类为TTInstrumentationTestCase,类所在的包为tt.android.test。
3、在重载了setUp和tearDown函数的测试类中,需要分别调用super.setUp()和super.tearDown()函数。
4、其他操作同Android JUnit Test操作相同。
如下为一个与上面Android JUnit Test单元测试类相对应的TT Android JUnit Test单元测试类:
package com.zeroone.unittest.test;
import tt.android.test.TTInstrumentationTestCase;
import com.zeroone.unittest.R; import com.zeroone.unittest.Sample;
import android.content.Intent; import android.os.SystemClock; import android.util.Log; import android.widget.Button; import android.widget.TextView;
public class SampleTest extends TTInstrumentationTestCase { private Sample sample = null; private Button button = null; private TextView text = null;
// 初始设置 @Override protected void setUp() { try { super.setUp(); } catch (Exception e) { e.printStackTrace(); } Intent intent = new Intent(); intent.setClassName("com.zeroone.unittest", 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); }
// 垃圾清理与资源回收 @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(1000); getInstrumentation().runOnMainSync(new PerformClick(button)); SystemClock.sleep(1000); assertEquals("Hello Android", text.getText().toString()); Log.v("testActivity", "test the Activity Over"); }
// 模拟按钮点击的接口 private class PerformClick implements Runnable { Button btn = null; public PerformClick(Button button) { btn = button; }
public void run() { btn.performClick(); } }
// 测试add方法 public void testAdd() throws Exception{ Log.v("testAdd", "test the add method"); int test = sample.add(1, 1); assertEquals(2, test); Log.v("testAdd", "test the add method over"); } }
|
3、TT单元测试的编译
编写完成TT Android JUnit Test 测试类之后,使用TT对工程进行编译。
1、修改编译文件,如果是单编码格式的工程,需要修改ant-build-a/android-instru_en.xml文件,多源码格式工程修改ant-build-a/android-instru_code_en.xml文件。修改内容为,添加-filter参数,修改方法为:
添加filterpath属性,用于过滤不需要插桩的单元测试驱动类文件。
<property location="不需要插桩编译的路径" name="filterpath"/>
…
<arg line=""${propath}" "${TT}" -tt="${testpropath}" -encoding=${encoding} -filter="${filterpath}" -s="${testpropath}/src""/>
在运行JavaParser处,添加绿色背景参数。
2、打开TT,创建一个新的工程,与非单元测试工程的编译相同。
4、TT单元测试的运行
将上一步中,TT编译生成的APK文件,安装到模拟器中,安装之后,会在Dev Tools工具下,Instrumentation中会出现与AndroidManifest.xml文件中android:label设置的同名的标签,点击可运行TT单元测试。
在运行TT单元测试时,需要打开TT接收数据,否则测试会阻塞在消息发送处而无法进行。
测试步骤为:
1、打开TT,选择将要运行的测试的工程,然后打开试试监控界面,新建一个测试用例。
2、打开模拟器。
3、进行端口重定向
双击运行adb-android.bat,模拟器打开后,只需运行一次,adb需要在环境变量中配置好,如果未配置,需要手动打开命令行窗口,运行:
adb forward tcp:15555 tcp:15555
adb forward tcp:15556 tcp:15556
adb forward tcp:15557 tcp:15557
adb forward tcp:15558 tcp:15558
如果运行adb forward –list出现:
其中,emulator-5554也可能是其他名称,则说明配置成功。
4、勾选上测试用例,set Ip为127.0.0.1,确定后点击开始。
5、在模拟器中打开Dev Tools,点击Instrumentation进入,点击工程中设置的标签名,如Tests for TT application.
点击后,开始运行运行单元测试。
此时TT实时监控界面会有数据的接收:
单元测试运行运行结束后,右击测试用例树右上角的刷新按钮,选择Update Testcase
会增加红色框中的测试用例,首先是与勾选的测试用例同级同名的测试类型,然后依次为测试类所在的包名,类名以及运行测试函数名。
在单元测试开始之前,被测程序的初始化数据会被保存在初始勾选的测试用例中,这里为UnitTest中。单元测试开始之后,产生的测试数据会被保存到,与测试驱动函数对应的测试用例当中。
TT单元测试完成。