Android单元测试

android源码中引入了java单元测试的框架(android源码目录:libcore\junit\src\main\java\junit\framework中可见),然后在java单元测试框架的基础上扩展属于android自己的测试框架。android具体框架类的关系图如下:

    Android单元测试_第1张图片

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

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

Android单元测试_第2张图片

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

 

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

 

1.Application的测试:

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

[java]  view plain copy
  1. package com.phicomm.hu;  
  2.   
  3. import android.app.Application;  
  4.   
  5. public class FxAndroidApplication extends Application  
  6. {  
  7.   
  8.     @Override  
  9.     public void onCreate()   
  10.     {  
  11.         // TODO Auto-generated method stub  
  12.         super.onCreate();  
  13.     }  
  14.   
  15.     @Override  
  16.     public void onTerminate()   
  17.     {  
  18.         // TODO Auto-generated method stub  
  19.         super.onTerminate();  
  20.     }  
  21.       
  22.     public String getFavourite()  
  23.     {  
  24.         return "I Love Java";  
  25.     }  
  26.   
  27. }  

 

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

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

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.phicomm.hu.test"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk android:minSdkVersion="10" />  
  8.   
  9.     <instrumentation  
  10.         android:name="android.test.InstrumentationTestRunner"  
  11.         android:targetPackage="com.phicomm.hu" />  
  12.   
  13.     <application  
  14.         android:icon="@drawable/ic_launcher"  
  15.         android:label="@string/app_name" >  
  16.         <uses-library android:name="android.test.runner" />  
  17.     </application>  
  18.   
  19. </manifest>  


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

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

    

[java]  view plain copy
  1. package com.phicomm.hu.test;  
  2.   
  3. import com.phicomm.hu.FxAndroidApplication;  
  4.   
  5. import android.app.Application;  
  6. import android.test.ApplicationTestCase;  
  7.   
  8. public class FxApplicationTest extends ApplicationTestCase<FxAndroidApplication>   
  9. {  
  10.   
  11.     private FxAndroidApplication AppTest;  
  12.     public FxApplicationTest()  
  13.     {  
  14.         //调用父类构造函数,且构造函中传递的参数为被测试的类  
  15.         super(FxAndroidApplication.class);  
  16.     }  
  17.   
  18.     @Override  
  19.     protected void setUp() throws Exception  
  20.     {  
  21.         // TODO Auto-generated method stub  
  22.         super.setUp();  
  23.         //获取application之前必须调用的方法  
  24.         createApplication();  
  25.         //获取待测试的FxAndroidApplication  
  26.         AppTest = getApplication();  
  27.     }  
  28.       
  29.     //测试FxAndroidApplication的getFavourite方法  
  30.     public void testGetFavourite()  
  31.     {  
  32.         /*验证预测值"I Love C++"是否等于实际值, 
  33.         由于实际值为"I love Java",所以此处测试结果为Failure*/  
  34.         assertEquals("I Love C++", AppTest.getFavourite());  
  35.     }  
  36.       
  37. }  


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

  测试方法:

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

  Android单元测试_第3张图片

通过上图的测试结果可知,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单元测试_第4张图片

   登录界面FxLoginActivity的代码如下:

  

[java]  view plain copy
  1. package com.phicomm.hu;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.view.View;  
  7. import android.view.View.OnClickListener;  
  8. import android.widget.Button;  
  9. import android.widget.EditText;  
  10.   
  11. public class FxLoginActivity extends Activity   
  12. {  
  13.       
  14.     private EditText userName;  
  15.     private EditText passWord;  
  16.       
  17.     /** Called when the activity is first created. */  
  18.     @Override  
  19.     public void onCreate(Bundle savedInstanceState)   
  20.     {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.main);  
  23.           
  24.         userName = (EditText)findViewById(R.id.name);  
  25.         passWord = (EditText)findViewById(R.id.psd);  
  26.           
  27.         Button login = (Button)findViewById(R.id.login);  
  28.         Button reset = (Button)findViewById(R.id.reset);  
  29.            
  30.          //监听登录按钮  
  31.          login.setOnClickListener(new OnClickListener() {  
  32.               
  33.             @Override  
  34.             public void onClick(View v)   
  35.             {  
  36.                 // TODO Auto-generated method stub  
  37.                 Intent intent = new Intent(FxLoginActivity.this, FxResultActivity.class);                 
  38.                 //通过intent传递登录信息到ResultActivity的界面中显示  
  39.                 intent.putExtra("userName", userName.getText().toString());  
  40.                 intent.putExtra("passWord", passWord.getText().toString());  
  41.                 //启动ResultActivity显示登录界面信息  
  42.                 startActivity(intent);  
  43.             }  
  44.         });  
  45.            
  46.          //监听重置按钮          
  47.          reset.setOnClickListener(new OnClickListener()   
  48.          {  
  49.               
  50.             @Override  
  51.             public void onClick(View v)   
  52.             {  
  53.                 // TODO Auto-generated method stub  
  54.                 resetInput();  
  55.             }  
  56.         });  
  57.     }  
  58.       
  59.     public void resetInput()  
  60.     {  
  61.         userName.setText("");  
  62.         passWord.setText("");  
  63.     }  
  64. }  


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

 

[html]  view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <EditText   
  8.         android:id="@+id/name"  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:hint="@string/name"/>  
  12.       
  13.      <EditText   
  14.         android:id="@+id/psd"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="wrap_content"  
  17.         android:hint="@string/psd"/>  
  18.        
  19.      <LinearLayout   
  20.          android:orientation="horizontal"  
  21.          android:layout_width="match_parent"  
  22.          android:layout_height="wrap_content"  
  23.          >  
  24.            
  25.          <Button   
  26.              android:id="@+id/login"  
  27.              android:layout_width="fill_parent"  
  28.              android:layout_height="wrap_content"  
  29.              android:layout_weight="1"  
  30.              android:text="@string/login"/>  
  31.            
  32.           <Button   
  33.              android:id="@+id/reset"  
  34.              android:layout_width="fill_parent"  
  35.              android:layout_height="wrap_content"  
  36.              android:layout_weight="1"  
  37.              android:text="@string/reset"/>  
  38.            
  39.      </LinearLayout>  
  40.   
  41. </LinearLayout>  


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

[java]  view plain copy
  1. package com.phicomm.hu;  
  2.   
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.os.Bundle;  
  6. import android.util.Log;  
  7. import android.widget.EditText;  
  8. import android.widget.TextView;  
  9.   
  10. public class FxResultActivity extends Activity   
  11. {  
  12.   
  13.       
  14.     private static final String TAG = "ResultActivity";  
  15.     @Override  
  16.     protected void onCreate(Bundle savedInstanceState)   
  17.     {  
  18.         // TODO Auto-generated method stub  
  19.         super.onCreate(savedInstanceState);  
  20.         setContentView(R.layout.result);  
  21.           
  22.         TextView result = (TextView)findViewById(R.id.result);  
  23.         //通过得到intent获取登录界面传来的信息  
  24.         Intent intent = getIntent();  
  25.         String userName = intent.getStringExtra("userName");  
  26.         String passWord = intent.getStringExtra("passWord");  
  27.         //将登录信息在页面中显示  
  28.         result.setText("用户名:" + userName + "\n" + "密码:" + passWord);      
  29.     }  
  30.   
  31.       
  32. }  

 

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

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

  

[java]  view plain copy
  1. package com.phicomm.hu.test;  
  2.   
  3.   
  4.   
  5. import android.app.Instrumentation;  
  6. import android.test.ActivityInstrumentationTestCase2;  
  7. import android.view.KeyEvent;  
  8. import android.widget.Button;  
  9. import android.widget.EditText;  
  10.   
  11. import com.phicomm.hu.FxLoginActivity;  
  12.   
  13. public class FxLoginActivityTest extends ActivityInstrumentationTestCase2<FxLoginActivity>   
  14. {  
  15.   
  16.     private Instrumentation mInstrumentation;  
  17.     private FxLoginActivity mLoginTest;  
  18.       
  19.     private EditText userName;  
  20.     private EditText passWord;  
  21.     private Button login;  
  22.     private Button reset;  
  23.       
  24.     public FxLoginActivityTest()  
  25.     {  
  26.         super(FxLoginActivity.class);  
  27.     }  
  28.   
  29.     //重写setUp方法,在该方法中进行相关的初始化操作  
  30.     @Override  
  31.     protected void setUp() throws Exception   
  32.     {  
  33.         // TODO Auto-generated method stub  
  34.         super.setUp();  
  35.         /**这个程序中需要输入用户信息和密码,也就是说需要发送key事件, 
  36.          * 所以,必须在调用getActivity之前,调用下面的方法来关闭 
  37.          * touch模式,否则key事件会被忽略 
  38.          */  
  39.         //关闭touch模式  
  40.         setActivityInitialTouchMode(false);  
  41.         mInstrumentation = getInstrumentation();  
  42.         //获取被测试的FxLoginActivity  
  43.         mLoginTest = getActivity();  
  44.           
  45.         //获取FxLoginActivity相关的UI组件  
  46.         userName = (EditText)mLoginTest.findViewById(com.phicomm.hu.R.id.name);  
  47.         passWord = (EditText)mLoginTest.findViewById(com.phicomm.hu.R.id.psd);  
  48.         login = (Button)mLoginTest.findViewById(com.phicomm.hu.R.id.login);  
  49.         reset = (Button)mLoginTest.findViewById(com.phicomm.hu.R.id.reset);       
  50.           
  51.     }  
  52.       
  53.     //该测试用例实现在测试其他用例之前,测试确保获取的组件不为空  
  54.     public void testPreConditions()  
  55.     {  
  56.         assertNotNull(mLoginTest);  
  57.         assertNotNull(userName);  
  58.         assertNotNull(passWord);  
  59.         assertNotNull(login);  
  60.         assertNotNull(reset);  
  61.     }  
  62.       
  63.     /**该方法实现在登录界面上输入相关的登录信息。由于UI组件的 
  64.      * 相关处理(如此处的请求聚焦)需要在UI线程上实现, 
  65.      * 所以需调用Activity的runOnUiThread方法实现。 
  66.      */  
  67.     public void input()  
  68.     {  
  69.         mLoginTest.runOnUiThread(new Runnable()   
  70.         {  
  71.               
  72.             @Override  
  73.             public void run()   
  74.             {  
  75.                 // TODO Auto-generated method stub  
  76.                 userName.requestFocus();  
  77.                 userName.performClick();  
  78.             }  
  79.         });  
  80.         /*由于测试用例在单独的线程上执行,所以此处需要同步application, 
  81.          * 调用waitForIdleSync等待测试线程和UI线程同步,才能进行输入操作。 
  82.          * waitForIdleSync和sendKeys不允许在UI线程里运行 
  83.          */  
  84.         mInstrumentation.waitForIdleSync();  
  85.           
  86.         //调用sendKeys方法,输入用户名  
  87.         sendKeys(KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_H,  
  88.                 KeyEvent.KEYCODE_I, KeyEvent.KEYCODE_C,  
  89.                 KeyEvent.KEYCODE_O, KeyEvent.KEYCODE_M,  
  90.                 KeyEvent.KEYCODE_M);  
  91.           
  92.         mLoginTest.runOnUiThread(new Runnable()   
  93.         {  
  94.               
  95.             @Override  
  96.             public void run()   
  97.             {  
  98.                 // TODO Auto-generated method stub  
  99.                 passWord.requestFocus();  
  100.                 passWord.performClick();  
  101.             }  
  102.         });  
  103.           
  104.         //调用sendKeys方法,输入密码  
  105.         sendKeys(KeyEvent.KEYCODE_1, KeyEvent.KEYCODE_2,   
  106.                 KeyEvent.KEYCODE_3, KeyEvent.KEYCODE_4);  
  107.     }  
  108.       
  109.     //测试输入的用户信息  
  110.     public void testInput()  
  111.     {  
  112.         //调用测试类的input方法,实现输入用户信息(sendKeys实现输入)  
  113.         input();  
  114.         //测试验证用户信息的预期值是否等于实际值  
  115.         assertEquals("phicomm", userName.getText().toString());  
  116.         //密码的预期值123与实际值1234不符,Failure;  
  117.         assertEquals("123", passWord.getText().toString());  
  118.     }  
  119.       
  120.     //测试登录按钮  
  121.     public void testLogin()  
  122.     {  
  123.         input();  
  124.         //开新线程,并通过该线程在实现在UI线程上执行操作  
  125.         mInstrumentation.runOnMainSync(new Runnable()   
  126.         {  
  127.               
  128.             @Override  
  129.             public void run()   
  130.             {  
  131.                 // TODO Auto-generated method stub  
  132.                 login.requestFocus();  
  133.                 login.performClick();  
  134.             }  
  135.         });  
  136.     }  
  137.       
  138.     //测试重置按钮  
  139.     public void testReset()  
  140.     {  
  141.         input();  
  142.         mInstrumentation.runOnMainSync(new Runnable()   
  143.         {  
  144.               
  145.             @Override  
  146.             public void run()   
  147.             {  
  148.                 // TODO Auto-generated method stub  
  149.                 reset.requestFocus();  
  150.                 //点击按钮  
  151.                 reset.performClick();  
  152.             }  
  153.         });  
  154.         //验证重置按钮的实现功能,是否点击后内容为空  
  155.         assertEquals("", userName.getText().toString());  
  156.         assertEquals("", passWord.getText().toString());  
  157.     }  
  158. }  

 

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

 

FxResultActivity的测试类代码如下:

[java]  view plain copy
  1. package com.phicomm.hu.test;  
  2.   
  3. import android.content.Intent;  
  4. import android.test.ActivityInstrumentationTestCase2;  
  5. import android.widget.TextView;  
  6.   
  7. import com.phicomm.hu.FxResultActivity;  
  8.   
  9. public class FxResultActivityTest extends ActivityInstrumentationTestCase2<FxResultActivity>   
  10. {  
  11.   
  12.     private static final String LOGIN_INFO = "用户名:feixun\n密码:123";  
  13.       
  14.     private FxResultActivity mResultActivity;  
  15.     private TextView result;  
  16.       
  17.     public FxResultActivityTest()  
  18.     {  
  19.         super(FxResultActivity.class);  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void setUp() throws Exception   
  24.     {  
  25.         // TODO Auto-generated method stub  
  26.         super.setUp();  
  27.         //创建Intent,通过Intent传递用户的登录信息  
  28.         Intent intent = new Intent();  
  29.         intent.putExtra("userName""feixun");  
  30.         intent.putExtra("passWord""123");  
  31.         //通过携带用户登录信息的intent启动FxResultActivity  
  32.         mResultActivity = launchActivityWithIntent("com.phicomm.hu",   
  33.                 FxResultActivity.class, intent);  
  34.         //获取UI组件  
  35.         result = (TextView)mResultActivity.findViewById(com.phicomm.hu.R.id.result);  
  36.     }  
  37.       
  38.     //测试验证用户的登录信息  
  39.     public void testLoginInfo()  
  40.     {  
  41.         //验证预期值是否等于实际值  
  42.         assertEquals(LOGIN_INFO, result.getText().toString());  
  43.     }  
  44.       
  45. }  


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

你可能感兴趣的:(android,单元测试)