◆版權聲明:本文出自胖喵~的博客,轉載必須注明出處。
轉載請注明出處:http://www.cnblogs.com/by-dream/p/5503645.html
前言
有朋友給我留言說,能否介紹一下Robotium這款框架。相信很多的朋友都聽過這個框架的名字吧,沒錯它也是國外的一款Android自動化框架,功能比較強大,但是我個人比較鍾愛谷歌原生的自動化框架,一方面是因為原生的自動化框架比較穩定,並且一直認為"谷歌出品,必是精品",另一方面很多的自動化框架都是對谷歌測試框架的再封裝,比如Robotium就是對Instrumentation的封裝。因此當對谷歌原生的框架有了了解后,其他框架你也就無師自通了。
這里我想說明一下,本節以及后面介紹的Instrumentation相關的內容,對學習者的要求比較高,要求學習者必須對Android開發的知識有所了解,否則理解起來會相當的困難。所以建議在學習本節之前,可以學習一下我寫的Android開發相關的前幾篇文章。
簡介
在Android2.3或者更早的版本中就已經有Instrumentation這個框架了,因此在那個時間段做過Android自動化測試的同學一定對這款框架特別的熟悉。看過上一節Test Concept的我們對基本概念和框架應該已經有了簡單的認識。接下來我會直接先用一個小例子來教大家如果使用這個框架。
首先認識兩個類,這兩個類就是我們后面會用到的兩個類:
InstrumentationTestcase:
ActivityInstrumentationTestCase2:
從類結構不難看出,下面的類是繼承自上面的類,簡單說明下是怎么回事,因為Android當中有四大組件:Activity、service、Content Provider和Broadcast Receiver ,而四大組件的特性都很分明,而Activity做為我們可見的與我們接觸最多的一個組件,我們在自動化的時候難免和它打交道比較多,因此將它單拉出來說明一下,我在最開始的例子中會首先使用它們的父類InstrumentationTestcase來進行講解。
源碼地址:http://124.16.141.157/lxr-0101/source/frameworks/base/core/java/android/test/InstrumentationTestCase.java?v=android-5.1
源碼地址:http://124.16.141.157/lxr-0101/source/frameworks/base/test-runner/src/android/test/?v=android-5.1
源程序
這里的源程序指的就是被測程序,也就是我們要測試App,這里我自己寫了一個簡單App,用來作為我們的測試demo。
這個App一共兩頁面。第一個頁面中我們可以輸入兩個整數,然后按下查看結果按鈕,就會跳到第二個頁面,第二個頁面就會顯示出結果。
第一個頁面的代碼和布局:

1 package com.bryan.calc; 2 3 import com.bryan.calc.R; 4 5 import android.app.Activity; 6 import android.content.Intent; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.view.View.OnClickListener; 10 import android.widget.Button; 11 import android.widget.EditText; 12 13 public class MainActivity extends Activity 14 { 15 // 定義變量 16 EditText num1 = null; 17 EditText num2 = null; 18 Button btn = null; 19 20 @Override 21 protected void onCreate(Bundle savedInstanceState) 22 { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.activity_main); 25 26 // 初始化控件 27 num1 = (EditText)findViewById(R.id.num1); 28 num2 = (EditText)findViewById(R.id.num2); 29 btn = (Button)findViewById(R.id.btn); 30 31 btn.setOnClickListener( new getRes()); 32 } 33 34 // 按鈕的事件響應 35 class getRes implements OnClickListener 36 { 37 @Override 38 public void onClick(View arg0) 39 { 40 int n1 = Integer.parseInt(num1.getText().toString()); 41 int n2 = Integer.parseInt(num2.getText().toString()); 42 GetResult(n1, n2); 43 } 44 } 45 46 // 得到結果,並且調起計算結果頁面 47 public void GetResult(int num1, int num2 ) 48 { 49 Intent intent = new Intent(); 50 intent.putExtra("res", num1+num2); 51 intent.setClass(MainActivity.this, ResultActivity.class); 52 startActivity(intent); 53 } 54 }

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:gravity="center_horizontal" 6 android:orientation="vertical"> 7 8 <EditText 9 android:id="@+id/num1" 10 android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:inputType="number" /> 13 14 <TextView 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" 17 android:text=" + " /> 18 19 <EditText 20 android:id="@+id/num2" 21 android:layout_width="wrap_content" 22 android:layout_height="wrap_content" 23 android:inputType="number" > 24 </EditText> 25 26 <Button 27 android:id="@+id/btn" 28 android:layout_width="wrap_content" 29 android:layout_height="wrap_content" 30 android:text="查看結果" /> 31 32 </LinearLayout>
第二個頁面的代碼和布局:

1 package com.bryan.calc; 2 3 import com.bryan.calc.R; 4 5 import android.app.Activity; 6 import android.content.Intent; 7 import android.os.Bundle; 8 import android.widget.TextView; 9 10 public class ResultActivity extends Activity 11 { 12 TextView res = null; 13 14 @Override 15 protected void onCreate(Bundle savedInstanceState) 16 { 17 super.onCreate(savedInstanceState); 18 setContentView(R.layout.activity_result); 19 20 // 獲得上一個頁面傳遞來的數據 21 Intent intent = getIntent(); 22 int resnum = intent.getIntExtra("res", 0); 23 24 res = (TextView)findViewById(R.id.res); 25 res.setText(resnum+""); 26 } 27 }

1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical"> 6 7 <TextView 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content" 10 android:text="結果是:" /> 11 12 <TextView 13 android:id="@+id/res" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" /> 16 17 </LinearLayout>
首先我們要確立要驗證什么?這個App中我們可以模擬輸入兩個假數據,然后去讓它計算結果然后在結果頁面驗證計算的結果對不對。
一般拿到被測程序的源代碼之后,需要關注的也就是和我們要驗證的內容相關的控件的信息和代碼的實現。
測試工程
被測工程可以直接在源代碼中添加,也可以獨立建立一個工程。一般情況下我們我們不會直接在開發的代碼中添加我們的測試代碼,因此這里我們介紹獨立工程的方法。我們來看下步驟:
因為我的源代碼是使用Eclipse的ADT開發的Android程序,因此這里我們也使用Eclipse來建立測試工程。選擇“New”-“Other”-“Android Test Project”
接着輸入測試工程的名稱:
選擇測試工程,這里需要將剛才我們的源程序給選進來。
點擊finish之后,我們的工程就建立好了。
建立完成之后有兩點需要注意一下,這兩點也是我們如果需要手動建立工程的時候,需要做的事情:
1、測試的包名是被測的包名+test:
2、Manifest文件當中引入的內容:
測試代碼
建立完工程之后,我們新建一個測試類。
新建的類需要繼承 InstrumentationTestCase,然后重寫 setUp() 方法和 tearDown() 方法。一般測試代碼需要的初始化數據都寫在setUp中,而測試結束后要釋放處理的一些邏輯寫到tearDown中,另外測試的腳本的函數以test來命名開頭即可。所以測試代碼的格式為:
public class calctest extends InstrumentationTestCase { @Override protected void setUp() throws Exception { super.setUp(); // 初始化代碼 } @Override protected void tearDown() throws Exception { super.tearDown(); // 釋放代碼 }// 測試代碼 public void test****() { } }
這里我直接貼出我的測試代碼吧,然后着重說說需要注意的地方
package com.bryan.calc.test; import com.bryan.calc.MainActivity; import com.bryan.calc.ResultActivity; import android.app.Instrumentation.ActivityMonitor; import android.content.Intent; import android.os.SystemClock; import android.test.InstrumentationTestCase; import android.view.KeyEvent; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class calctest extends InstrumentationTestCase { MainActivity mainActivity; EditText num1 = null; EditText num2 = null; Button button = null; @Override protected void tearDown() throws Exception { SystemClock.sleep(4000); mainActivity.finish(); super.tearDown(); } @Override protected void setUp() throws Exception { super.setUp(); Intent intent = new Intent(); intent.setClassName("com.bryan.calc", MainActivity.class.getName()); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mainActivity = (MainActivity) getInstrumentation().startActivitySync(intent); num1 = (EditText) mainActivity.findViewById(com.bryan.calc.R.id.num1); num2 = (EditText) mainActivity.findViewById(com.bryan.calc.R.id.num2); button = (Button) mainActivity.findViewById(com.bryan.calc.R.id.btn); } public void testActivity() { mainActivity.runOnUiThread(new Runnable() { @Override public void run() { num1.setText("22"); num2.setText("33"); } }); ActivityMonitor am = getInstrumentation().addMonitor("com.bryan.calc.ResultActivity", null, false); getInstrumentation().runOnMainSync(new Runnable() { @Override public void run() { button.performClick(); } }); ResultActivity resultActivity = (ResultActivity) getInstrumentation().waitForMonitorWithTimeout(am, 2000); getInstrumentation().removeMonitor(am); assertNotNull("Get the instance of ResultActivity is failed.", resultActivity); TextView textView = (TextView) resultActivity.findViewById(com.bryan.calc.R.id.res); assertEquals("8", textView.getText().toString()); } }
一、啟動App
無論我們做什么測試之前,第一步都是啟動App,之前monkeyrunner和uiautomator的啟動方法都是根據包名+Activity名使用外部的start來喚起這個App,而我們使用Instrumentation的就可以直接通過Intent來啟動,如果有對Intent不熟悉的同學,建議自己去看看Intent在Android當中扮演的角色。
二、獲取控件
當我們啟動了App之后,接下來做的就是對控件進行操作,這里獲取控件的方法和實際Android開發當中的獲取方法是一致的,因為第一步我們已經拿到了當前Activity的實例。這里需要注意的就是,資源文件R我們需要引入的是原工程當中的R文件,因此我們在代碼中,可以像下面這樣的寫法來完成。
三、控件操作
上面的例子當中,我首先是給EditText賦值,然后去點擊Button,這兩個剛好具有代表性,因為Android是非線程安全的,因此如果不在主線程(也就是UI線程)當中對控件進行操作,那么就會發生異常,因此我們的測試程序在操作、使用這些控件的時候需要注意必須在UI線程中完成。非UI線程如果需要在UI線程操作的時候,一般有兩種寫法,這里我都列了出來:
四、獲取新出現的Activity
從上面可以看出當我們拿到了當前頁面的Activity的對象后,就可以通過findviewbyid來拿到控件的對象,一旦拿到控件的實例,就可以進行自動化了,但是呢?第一個Activity是通過startActivitySync()的返回值拿到的,那后面新出現的Activity並不是我們自己startActivity起來的,而是源程序當中代碼調用起來的,那么我們怎么拿到它的實例呢?這里就需要介紹一下下面的Monitor了。
拿到了新頁面的Activity的對象后,就可以用同樣方法再獲取新頁面上的空間了:
五、斷言
前面做了那么多的操作步驟,那么我們究竟要怎么驗證呢?當然是通過斷言
斷言的結果會直接影響到這條case的成功與否,下面的"運行結果" 會詳細解釋的。
運行結果
我們寫完代碼之后,我們有兩種方式可以觸發它執行。
IDE中觸發:
一種是直接在IDE中進行。這里以eclipse為例,就是需要右擊測試工程,然后選擇 "Run as" -> "Android JUnit Test"
這時候左邊區域出現一個JUint的區域,顯示這個結果,如下圖:
Runs 代表一共有多少個測試用例,現在執行了多少。
Errors 代表目前執行過的case有多少是錯誤的,這個錯誤並不是說case沒有通過,一般指的是代碼當中出現了異常,也就是說你代碼寫的有問題
Failures 代表就是斷言失敗的次數。這里假如我把期望值寫成和真實值不一樣的,運行后就可以看到Failures的個數是1,並且下發條也變成紅色的了:
命令行觸發:
