Activity Testing 翻譯

Activity 測試比較特殊,它依賴於 Android instrumentation 框架。與其它組件不一樣 Activity 基於一些回調函數有一個複雜的生命週期;除非用 instrumentation 咱們不能直接調用這些回調函數。另外,從程序向用戶接口發送事件的惟一方法也是用 instrumentation。html

該文檔描述了和用 instrumentation 和其它測試工具來測試 Activity。這裏假設你已經閱讀了 Testing Fundamentals 中介紹 Andoid 測試和 instrumentation 框架的部分。java

The Activity Testing APIandroid


Activity 測試的基類就是 InstrumentationTestCase,它爲用於測試 Activity 的 test case 子類提供 instrumentation。app

 

這個基類爲針對 Activity 的測試提供如下功能:框架

  • 控制生命週期:有了 instrumentation,咱們能夠用 test case 類提供的函數控制待測 Activity 的啓動、暫停、銷燬。
  • 依賴注入:經過 instrumentation,咱們能夠建立諸如 Context、Application 這些系統對象的模擬對象,並用這些模擬對象運行待測 Activity。這有助於咱們控制測試環境,並和產品環境分離。咱們還能夠自定義 Intent 並用它啓動一個 Activity。
  • 和用戶接口交互:經過 instrumentation 咱們能夠直接向待測 Activity 的 UI 發送按鍵或者觸摸事件。

經過繼承 TestCase 和 Asser,這些 Activity 測試類一樣有 JUnit 框架的功能。eclipse

主要的兩個測試子類是 ActivityInstrumentationTestCase2 和 ActivityUnitTestCase。針對那些經過非標準模式加載的 Activity ,咱們要用 SingleLaunchActivityTestCase 來測試。ide

ActivityInstrumentationTestCase2函數

ActivityInstrumentationTestCase2 這個 test case 類用一個通用的底層系統來對一個應用程序中的一個或多個 Activity 進行功能測試。它用一個標準的系統 Context(非模擬的),在待測應用的一個通用實例(非模擬的)中運行 Activity。它容許你向待測 Activity 發送模擬 Intent,所以用過它,你能夠對一個能響應多種 intent 的 Activity 進行測試,或這對一個想從 Intent 中取得某種數據的Activity 進行測試,又或者兩種功能都有的 Activity 也能夠。可是要注意,因爲它不容許模擬 Context ,Application 對象,因此測試不能獨立於生產系統。工具

ActivityUnitTestCase單元測試

ActivityUnitTestCase 這個 test case 類用來對一個 Activity 進行獨立測試。在啓動 Activity 以前,能夠注入一個模擬 Context 或者 Application。經過它咱們能夠在一個獨立的環境中運行一個 Activity 測試,能夠對一些和 Android 系統沒有交互的函數進行單元測試。儘管經過調用 Activity.startActivity(Intent) 函數咱們能夠查看所接收的參數,可是咱們不能發送一個模擬 Intent 給待測 Activity。

SingleLanunchActivityTestCase

SingleLanunchActivityTestCase 能夠很方便地對單個 Activity 進行測試,而且所在的測試環境不會在過程當中被重置。它對 setUp() 和 tearDown() 的調用只有一次,而不是對每一個測試函數都調用一次。它不容許注入任何模擬對象。

這個 test case 更有利於測試那些運行在非 standard 模式的 Activity。它能夠確保測試的 fixture 不會在測試的工程中被重置。因此它能夠對 Activity 中多個相關聯的功能進行測試。

Mock objects and activity testing

這一部分介紹在 Activity 測試中模擬對象的用法,它們定義在 android.test.mock 包中。

模擬對象 MockApplication 僅用在使用 ActivityUnitTestCase 測試 Activity 的狀況。默認狀況下,ActivityUnitTestCase 會建立一個隱藏的 MockApplication 用來代替待測應用的 Application 對象。咱們能夠經過 setApplication() 函數注入本身的對象。

Assertions for activity testing

ViewAsserts 類中定義了一些針對 View 的斷言。能夠用它來驗證 View 對象的對齊方式和位置,而且能夠查看 ViewGroup 對象的狀態。

What To Test


  • 輸入驗證:一個 Activity 可否對 EditText View 中輸入的值作出正確的響應。作一個按鍵事件序列,發給 Activity,而後用 findViewById(int) 查看 View 的狀態。你能夠驗證一個正確的按鍵事件序列可否激活 OK 按鈕,而一個錯誤的序列可否把按鈕變爲 disabled 狀態。你也能夠在 View 設置一些錯誤信息來測試 Acivity 的響應。
  • 生命週期事件:測試應用程序中的每一個 Activity 是否能正確處理生命週期事件。通常狀況下,生命週期事件是由用戶或者系統的一些動做引發,這些事件會激發一個諸如 onCreate() 或者 onClick() 函數。例如,一個 Activity 應當在響應暫停或者銷燬時保存它的狀態。要記得即便是屏幕方向的一個改變也會致使當前 Activity 被銷燬,因此你應針對一些特殊的設備動做進行測試,以確認應用程序的狀態不會被意外丟失。
  • Intents:測試每個 Activity 可否正確處理 manifest 文件中 intent 過濾器所列的 Intent。你能夠用 ActivityInstrumentationTestCase2 向待測 Activity 發送模擬 Intent。
  • 運行時配置變動:當程序正在運行的時候改變設備的配置,來測試每個 Activity 可否正確響應。這些配置包括設備的方向,當前語言等等。如何處理這些變化,詳情請參考 Handling Runtime Changes
  • 屏幕大小好分辨率:在發佈你的應用程序以前,請確保測試你想運行的全部屏幕大小和密度的設備。你能夠用多個 AVD 來測試多個尺寸和密度的屏幕,也能夠直接在所需設備上測試。更多詳情請參考 Supporting Multiple Screens

Next Steps


要了解如何在 Eclipse 中設置和運行測試,請參考 Testing from Eclipse with ADT。若是你用的不是 Eclipse 請參考 Testing from Other IDEs

若是你要一步一步地學習測試 Activity 。請參考 Activity Testing Tutorial,它指導你針對一個 Activity-oriented 應用建立一個完整的測試方案。

Appendix: UI Testing Notes


接下來這幾節講解測試 Android 應用 UI 的一些要點,特別對在測試過程當中如何處理來自於 UI 線程的動做,觸摸、按鍵事件,解鎖 Home Screen 有很大幫助。

Testing on the UI thread

應用程序的 Activity 運行在應用程序的 UI 線程中。一旦 UI 被初始化,好比在 Activity 的 onCreate() 函數中,以後全部和 UI 的交互都必須在 UI 線程中進行。以正常的方式運行程序,它有權限訪問該線程也沒必要作什麼特別的操做。

當運行一個應用程序的測試的時候就有所不一樣了。經過基於 instrumentation 的類,咱們能夠調用待測應用 UI 相關的函數。而其它的測試類就不容許這麼幹。能夠用 @UIThreadTest 給函數註釋讓整個函數在 UI 線程中運行。注意這樣作將使函數的全部語句都運行在 UI 線程中。那些和 UI 沒有交互的函數不容許這樣作;例如,不能(在 UI 線程中)調用 Instrumentation.waitForIdleSync() 函數。

若是想讓測試函數的一部分語句子在 UI 線程中運行,就要建立一個 Runnable 匿名類,而後把想(在 UI 線程中)運行的語句放在 run() 函數中,並把該類的實例做爲參數傳給 appActivity.runOnUiThread() 函數,這個appActivity 就是待測 Activity 的實例。

例以下面的代碼:實例化一個 Activity 用來測試,爲該 Activity 中顯示的一個 Spinner 請求焦點,而後發送一個按鍵事件給它。要注意的是:不容許waiForIdleSync 函數和 sendKeys 函數運行在 UI 線程中。

private MyActivity mActivity; // MyActivity is the class name of the app under test
private Spinner mSpinner;
...
protected void setUp() throws Exception {
      super.setUp();
      mInstrumentation = getInstrumentation();
      mActivity = getActivity(); // get a references to the app under test
      /*       * Get a reference to the main widget of the app under test, a Spinner       */
      mSpinner = (Spinner) mActivity.findViewById(com.android.demo.myactivity.R.id.Spinner01);
      ... 
}

public void aTest() {
      /*       * request focus for the Spinner, so that the test can send key events to it
       * This request must be run on the UI thread. To do this, use the runOnUiThread method
       * and pass it a Runnable that contains a call to requestFocus on the Spinner.
       */
      mActivity.runOnUiThread(new Runnable() {
          public void run() {
              mSpinner.requestFocus();
          }
      });
      mInstrumentation.waitForIdleSync();
      this.sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);

Turning off touch mode

若是要用測試代碼向設備或者模擬器發送按鍵事件,從而控制它們,那麼必須關掉它們的觸摸模式,不然這些按鍵事件就會被忽略掉。

在調用 getActivity() 啓動 Activity 以前調用 ActivityInstrumentationTestCase2.setActivityTouchMode(false) 就能夠關掉觸摸模式。必須在一個非 UI 線程中運行的函數中調用該函數。所以,你不能在一個有 @UIThread 註釋的函數中調用這個觸摸模式函數,而應該在 setUP() 函數中調用。

Unlocking the emulator or device

你會發現,若是模擬器或者設備的 home sceen 處於鍵盤鎖鎖定狀態 UI 測試就沒法正常工做。這是由於待測應用沒法收到從 sendKeys() 發送的按鍵事件。爲了不這個問題,最好的方法是在模擬器或設備啓動後就關閉 home screen 的鍵盤鎖。

也能夠顯式地關閉這個鍵盤鎖。要這樣作須要在 manifest 文件中加一個權限,而後在待測應用程序中關閉鍵盤鎖。可是要注意,在發佈這個應用程序以前也必須把這些代碼移出,或者關閉這些代碼。

加這個權限,就是在 <manifest> 節點加一個子節點 <uses-permission android:name=」android.permission.DISABLE_KEYGUARD」>。而後在待測 Activity 的 onCreate()函數中加入下面代碼來關閉鍵盤鎖:

mKeyGuardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
mLock = mKeyGuardManager.newKeyguardLock("activity_classname");
mLock.disableKeyguard();

其中的 activity_classname 就是待測 Activity 的類名。

Trobleshooting UI tests

這一節列出了咱們在 UI 測試中會常常遇到的失敗的(failures) 測試以及緣由:

WrongThreadException

Problem:

對於這個失敗的測試,失敗棧信息中有下面的錯誤信息:

android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Probable Cause:

若是在一個 UI 線程以外向該 UI 線程發送 UI 事件就會常常發生這種錯誤。這一般是由於你在測試代碼中發送 UI 事件,而又沒有用 @UIThread 註釋也沒有用 runOnUiThead() 函數。就是說測試函數企圖在 UI 線程以外和 UI 作交互。

Sugested Resolution:

在 UI 線程總進行 UI 交互。用有 instrumentation 的 test case 類。詳細信息請參考前面的 Testing on the UI Thread 章節。

java.lang.RuntimeException

Problem:

對於這個失敗的測試,失敗棧信息中有下面的錯誤信息:

java.lang.RuntimeException: This method can not be called from the main application thread

Probable Cause:

若是你的測試函數已經有了 @UiThreadTest 註釋,可是又企圖作一些 UI 線程之外的事情,或者又去調用 runOnUiThread() 函數。

Suggested Resolution:

移除 @UiThreadTest 註釋,不要調用 runOnUiThead() 函數,或者重構你的測試。

相關文章
相關標籤/搜索