Android编程单元测试实例详解(附源码)

时间:2022-05-31 06:50:52

本文实例讲述了android编程单元测试。分享给大家供大家参考,具体如下:

完整实例代码代码点击此处本站下载

本文是在上一篇文章《java编程之单元测试(junit)实例分析》的基础上继续讲解android的单元测试,android源码中引入了java单元测试的框架(android源码目录:libcore\junit\src\main\java\junit\framework中可见),然后在java单元测试框架的基础上扩展属于android自己的测试框架。android具体框架类的关系图如下:

Android编程单元测试实例详解(附源码)

从上图的类关系图中可以知道,通过android测试类可以实现对android中相关重要的组件进行测试(如activity,service,contentprovider,甚至是application)。

其实在android源码中,基本上每个系统应用都自带一个测试工程,如下图的源码中settings(设置)模块:

Android编程单元测试实例详解(附源码)

上图的tests文件夹中就是settings模块自带的单元测试工程,有兴趣的读者可自行去研读一下源代码。

eclipse下(当然,前提是要保证eclipse中相关的android环境已经搭建好)进行android单元测试:

1.application的测试:

新建一个android项目,在该android项目添加一个继承application的类,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.phicomm.hu;
import android.app.application;
public class fxandroidapplication extends application
{
 @override
 public void oncreate()
 {
  // todo auto-generated method stub
  super.oncreate();
 }
 @override
 public void onterminate()
 {
  // todo auto-generated method stub
  super.onterminate();
 }
 public string getfavourite()
 {
  return "i love java";
 }
}

appication类创建好后,接着创建对应的测试工程:选中其所在的android工程---->鼠标右键----->new---->android test project----->输入测试工程名--->next----->选择被测试的目标android工程(此处为fxandroidapplication所在的android工程)。这样,一个测试工程就创建完成了。

通过eclipse创建自动生成的测试工程项目和android工程项目结构上没什么大的区别,主要是在androidmanifest.xml中有变化,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
 package="com.phicomm.hu.test"
 android:versioncode="1"
 android:versionname="1.0" >
 <uses-sdk android:minsdkversion="10" />
 <instrumentation
  android:name="android.test.instrumentationtestrunner"
  android:targetpackage="com.phicomm.hu" />
 <application
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name" >
  <uses-library android:name="android.test.runner" />
 </application>
</manifest>

在androidmanifest.xml注册了相关的测试环境(这些是android独有的):<uses-library android:name="android.test.runner" />实现使用相关的运行测试类库,<instrumentation />中的targetpackage为被测试类所在的包。

接下来在测试工程中创建fxandroidapplicationd的测试类,代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.phicomm.hu.test;
import com.phicomm.hu.fxandroidapplication;
import android.app.application;
import android.test.applicationtestcase;
public class fxapplicationtest extends applicationtestcase<fxandroidapplication>
{
 private fxandroidapplication apptest;
 public fxapplicationtest()
 {
  //调用父类构造函数,且构造函中传递的参数为被测试的类
  super(fxandroidapplication.class);
 }
 @override
 protected void setup() throws exception
 {
  // todo auto-generated method stub
  super.setup();
  //获取application之前必须调用的方法
  createapplication();
  //获取待测试的fxandroidapplication
  apptest = getapplication();
 }
 //测试fxandroidapplication的getfavourite方法
 public void testgetfavourite()
 {
  /*验证预测值"i love c++"是否等于实际值,
  由于实际值为"i love java",所以此处测试结果为failure*/
  assertequals("i love c++", apptest.getfavourite());
 }
}

测试类创建好后,就可以实现对fxandroidapplicationd进行测试了。

测试方法:

启动android模拟器(也可以通过android手机)----->运行android工程----->在测试工程中选中测试类fxapplicationtest---->鼠标右键--->run as---->android junit test。这样,测试结果就可以在eclipse的junit视图上显示了,如下图:

Android编程单元测试实例详解(附源码)

通过上图的测试结果可知,applicationtestcase测试类中有两个测试方法是默认进行测试的(testgetfavourite才是我们要测试的方法)。

当然,还可以通过adb进行测试:连接android手机------>打开电脑命令窗口(开始-->运行--->输入cmd)---->在命令窗口输入adb shell---->am instrument -w com.phicomm.hu.test(测试用例所在的包名)/android.test.instrumentationtestrunner。

2.activity的测试:

和上面application一样,先创建一个android工程,该工程中创建了两个activity,一个activity实现输入用户信息的登录界面,另一个acticity显示输入的用户信息。

效果图如下:

Android编程单元测试实例详解(附源码)

登录界面fxloginactivity的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.phicomm.hu;
import android.app.activity;
import android.content.intent;
import android.os.bundle;
import android.view.view;
import android.view.view.onclicklistener;
import android.widget.button;
import android.widget.edittext;
public class fxloginactivity extends activity
{
 private edittext username;
 private edittext password;
 /** called when the activity is first created. */
 @override
 public void oncreate(bundle savedinstancestate)
 {
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.main);
  username = (edittext)findviewbyid(r.id.name);
  password = (edittext)findviewbyid(r.id.psd);
  button login = (button)findviewbyid(r.id.login);
  button reset = (button)findviewbyid(r.id.reset);
   //监听登录按钮
   login.setonclicklistener(new onclicklistener() {
   @override
   public void onclick(view v)
   {
    // todo auto-generated method stub
    intent intent = new intent(fxloginactivity.this, fxresultactivity.class);    
    //通过intent传递登录信息到resultactivity的界面中显示
    intent.putextra("username", username.gettext().tostring());
    intent.putextra("password", password.gettext().tostring());
    //启动resultactivity显示登录界面信息
    startactivity(intent);
   }
  });
   //监听重置按钮  
   reset.setonclicklistener(new onclicklistener()
   {
   @override
   public void onclick(view v)
   {
    // todo auto-generated method stub
    resetinput();
   }
  });
 }
 public void resetinput()
 {
  username.settext("");
  password.settext("");
 }
}

main.xml布局文件的代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:orientation="vertical" >
 <edittext
  android:id="@+id/name"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:hint="@string/name"/>
  <edittext
  android:id="@+id/psd"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:hint="@string/psd"/>
  <linearlayout
   android:orientation="horizontal"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   >
   <button
    android:id="@+id/login"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/login"/>
   <button
    android:id="@+id/reset"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/reset"/>
  </linearlayout>
</linearlayout>

显示用户信息界面的fxresultactivity代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.phicomm.hu;
import android.app.activity;
import android.content.intent;
import android.os.bundle;
import android.util.log;
import android.widget.edittext;
import android.widget.textview;
public class fxresultactivity extends activity
{
 private static final string tag = "resultactivity";
 @override
 protected void oncreate(bundle savedinstancestate)
 {
  // todo auto-generated method stub
  super.oncreate(savedinstancestate);
  setcontentview(r.layout.result);
  textview result = (textview)findviewbyid(r.id.result);
  //通过得到intent获取登录界面传来的信息
  intent intent = getintent();
  string username = intent.getstringextra("username");
  string password = intent.getstringextra("password");
  //将登录信息在页面中显示
  result.settext("用户名:" + username + "\n" + "密码:" + password); 
 }
}

以上的android工程创建好后,创建一个对应的测试工程:

测试工程中对应的fxloginactivity类的测试代码如下(详细的代码讲解见代码中的相关注释,这里不在累赘):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package com.phicomm.hu.test;
import android.app.instrumentation;
import android.test.activityinstrumentationtestcase2;
import android.view.keyevent;
import android.widget.button;
import android.widget.edittext;
import com.phicomm.hu.fxloginactivity;
public class fxloginactivitytest extends activityinstrumentationtestcase2<fxloginactivity>
{
 private instrumentation minstrumentation;
 private fxloginactivity mlogintest;
 private edittext username;
 private edittext password;
 private button login;
 private button reset;
 public fxloginactivitytest()
 {
  super(fxloginactivity.class);
 }
 //重写setup方法,在该方法中进行相关的初始化操作
 @override
 protected void setup() throws exception
 {
  // todo auto-generated method stub
  super.setup();
  /**这个程序中需要输入用户信息和密码,也就是说需要发送key事件,
   * 所以,必须在调用getactivity之前,调用下面的方法来关闭
   * touch模式,否则key事件会被忽略
   */
  //关闭touch模式
  setactivityinitialtouchmode(false);
  minstrumentation = getinstrumentation();
  //获取被测试的fxloginactivity
  mlogintest = getactivity();
  //获取fxloginactivity相关的ui组件
  username = (edittext)mlogintest.findviewbyid(com.phicomm.hu.r.id.name);
  password = (edittext)mlogintest.findviewbyid(com.phicomm.hu.r.id.psd);
  login = (button)mlogintest.findviewbyid(com.phicomm.hu.r.id.login);
  reset = (button)mlogintest.findviewbyid(com.phicomm.hu.r.id.reset); 
 }
 //该测试用例实现在测试其他用例之前,测试确保获取的组件不为空
 public void testpreconditions()
 {
  assertnotnull(mlogintest);
  assertnotnull(username);
  assertnotnull(password);
  assertnotnull(login);
  assertnotnull(reset);
 }
 /**该方法实现在登录界面上输入相关的登录信息。由于ui组件的
  * 相关处理(如此处的请求聚焦)需要在ui线程上实现,
  * 所以需调用activity的runonuithread方法实现。
  */
 public void input()
 {
  mlogintest.runonuithread(new runnable()
  {
   @override
   public void run()
   {
    // todo auto-generated method stub
    username.requestfocus();
    username.performclick();
   }
  });
  /*由于测试用例在单独的线程上执行,所以此处需要同步application,
   * 调用waitforidlesync等待测试线程和ui线程同步,才能进行输入操作。
   * waitforidlesync和sendkeys不允许在ui线程里运行
   */
  minstrumentation.waitforidlesync();
  //调用sendkeys方法,输入用户名
  sendkeys(keyevent.keycode_p, keyevent.keycode_h,
    keyevent.keycode_i, keyevent.keycode_c,
    keyevent.keycode_o, keyevent.keycode_m,
    keyevent.keycode_m);
  mlogintest.runonuithread(new runnable()
  {
   @override
   public void run()
   {
    // todo auto-generated method stub
    password.requestfocus();
    password.performclick();
   }
  });
  //调用sendkeys方法,输入密码
  sendkeys(keyevent.keycode_1, keyevent.keycode_2,
    keyevent.keycode_3, keyevent.keycode_4);
 }
 //测试输入的用户信息
 public void testinput()
 {
  //调用测试类的input方法,实现输入用户信息(sendkeys实现输入)
  input();
  //测试验证用户信息的预期值是否等于实际值
  assertequals("phicomm", username.gettext().tostring());
  //密码的预期值123与实际值1234不符,failure;
  assertequals("123", password.gettext().tostring());
 }
 //测试登录按钮
 public void testlogin()
 {
  input();
  //开新线程,并通过该线程在实现在ui线程上执行操作
  minstrumentation.runonmainsync(new runnable()
  {
   @override
   public void run()
   {
    // todo auto-generated method stub
    login.requestfocus();
    login.performclick();
   }
  });
 }
 //测试重置按钮
 public void testreset()
 {
  input();
  minstrumentation.runonmainsync(new runnable()
  {
   @override
   public void run()
   {
    // todo auto-generated method stub
    reset.requestfocus();
    //点击按钮
    reset.performclick();
   }
  });
  //验证重置按钮的实现功能,是否点击后内容为空
  assertequals("", username.gettext().tostring());
  assertequals("", password.gettext().tostring());
 }
}

运行该测试类进行测试(选中---->run as--->android junit test),然后会自动启动模拟器进行相关的输入点击测试。注:测试时可以发现,程序在测试到testlogin()方法登录到另一个界面时,测试就停止了,也就是说testreset()没测试到。所以,需要测试testreset()时可以先把testlogin()注释掉,不然程序会测试到testlogin()后就不在对testreset()进行测试。

fxresultactivity的测试类代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.phicomm.hu.test;
import android.content.intent;
import android.test.activityinstrumentationtestcase2;
import android.widget.textview;
import com.phicomm.hu.fxresultactivity;
public class fxresultactivitytest extends activityinstrumentationtestcase2<fxresultactivity>
{
 private static final string login_info = "用户名:feixun\n密码:123";
 private fxresultactivity mresultactivity;
 private textview result;
 public fxresultactivitytest()
 {
  super(fxresultactivity.class);
 }
 @override
 protected void setup() throws exception
 {
  // todo auto-generated method stub
  super.setup();
  //创建intent,通过intent传递用户的登录信息
  intent intent = new intent();
  intent.putextra("username", "feixun");
  intent.putextra("password", "123");
  //通过携带用户登录信息的intent启动fxresultactivity
  mresultactivity = launchactivitywithintent("com.phicomm.hu",
    fxresultactivity.class, intent);
  //获取ui组件
  result = (textview)mresultactivity.findviewbyid(com.phicomm.hu.r.id.result);
 }
 //测试验证用户的登录信息
 public void testlogininfo()
 {
  //验证预期值是否等于实际值
  assertequals(login_info, result.gettext().tostring());
 }
}

运行上面的测试类,结果正确。

希望本文所述对大家android程序设计有所帮助。