原文地址:http://developer.android.com/tools/testing/testing_android.html
android 測試架構是開發環境的一部分,它提供了 an architecture 以及協助開發人員測試應用中從單元到架構每個部分的有用工具。
該測試架構有如下的重要特徵:
- android測試套件基於JUnit。你可以用純粹的JUnit來測試一個類,或者用android的JUnit擴充來測試android組件。如果你剛開始接觸android測試,你可以從使用一般用途的
AndroidTestCase類開始,然後再使用更複雜的類。
- android的JUnit擴充提供了針對不同組件的測試類別(test case classes)。這些類提供了建立虛擬對象的方法以及協助開發人員控制組件生命週期的方法。
- 測試套件(test suites)都包含在類似於主應用程式套件組合的測試包中,所以你不必重新學習一套設計和編譯測試的工具和技術。
- 編譯和測試的SDK工具在裝有ADT的eclipse中可用,在使用其它IDE時也可以通過命令列的形式使用。這些工具從待測試應用的工程中取得資訊並且自動為測試包建立編譯檔案、manifest檔案以及檔案目錄。
- SDK提供了monkeyrunner工具,它是一種用python來測試裝置的API。SDK同時提供了UI/Application Exerciser Monkey工具,它是一種通過發送偽隨機事件給裝置來對UI進行壓力測試的命令列工具。
本文檔描述了android測試架構的基礎,包含測試代碼的結構、用來開發測試的API以及開發人員用來啟動測試和查看測試結果的工具。本文檔假設你有android應用編程以及JUnit測試的基礎。
下面這張圖簡單描繪了andorid 的測試架構:
測試結構
android的編譯和測試載入器假設測試工程都已經組織成類似測試、測試類別、測試包和測試工程這樣的標準結構。
android測試基於JUnit。通常,一項JUnit測試就是一個用來測試“待測試應用某一部分”的方法。開發人員將這些測試方法放入稱為測試項 test cases (或者測試套件 test suites)的類中。每一項測試都是待測試應用中單獨模組的一個獨立測試,每個類是相近的測試方法的容器,通常它也提供額外的輔助方法。
在JUnit中,開發人員編譯一個或者多個測試源檔案到一個class檔案中。類似地,在android中開發人員用android的編譯工具編譯測試包中一個或者多個測試源檔案為class檔案。在JUnit 中,開發人員用 test runner 來執行測試類別 (test classes)。在android中,開發人員可以使用測試載入器載入測試包和待測試應用程式,之後測試載入器會調用android特有的 test runner。
測試工程
類似於android應用,測試是以工程的形式組織的。
一個測試工程就是一個目錄或者eclipse 工程,開發人員在其中建立原始碼、manifest檔案以及測試包的其它檔案。android SDK 為開發人員提供了建立和更新測試工程的工具,包含命令列和帶有ADT的eclipse 兩種形式。工具可以協助建立用來存放測試工程的原始碼、資源檔和manifest檔案的目錄。命令列工具同時會建立ant編譯檔案。
開發人員總應該用android工具來建立一個測試工程,工具可以做到:
- 自動協助配置測試包使用
InstrumentationTestRunner作為作為 test case runner,開發人員必須使用InstrumentationTestRunner或者它的子類來執行JUnit
測試。
- 為測試包取一個合適的名字。如果待測試應用程式套件名為com.mydomain.myapp,android 工具會自動命名測試包名為com.mydomain.myapp.test ,這可以協助開發人員識別它們之間的關係,同時也避免了衝突。
- 自動為測試工程建立合適的編譯檔案、manifest 檔案以及目錄結構。它協助開發人員在無需修改編譯檔案和配置測試包和待測試應用程式間關係的情況下編譯測試包。
你可以再檔案系統的任意地方建立一個測試工程,但是最好的做法是添加一個測試工程,這樣測試工程的根目錄 tests/ 和待測試應用的 src/目錄處於同一層級,這將方便你找到與應用程式相關聯的測試項。例如,如果你的應用程式工程的根目錄是 MyProject ,那麼你應該用如下的目錄結構:
MyProject/ AndroidManifest.xml res/ ... (resources for main application) src/ ... (source code for main application) ... tests/ AndroidManifest.xml res/ ... (resources for tests) src/ ... (source code for tests)
測試API
android 測試API基於JUnit測試API 擴充了一個instrumentation 架構和android特有的測試類別。
JUnit
你可以使用JUnit的TestCase類對未使用android API的類進行單元測試,TestCase也是AndroidTestCase的基類,你可以用它來測試依賴於android的對象。除了提供JUnit架構,AndroidTestCase提供了android特有的
setup、teardown以及其它的方法。
你可以使用JUnit的Assert類來顯示結果,assert方法將你的期望值與實際結果比較,在比較失敗時拋出異常。android同樣提供了一個擴充了比較類型的斷言類,以及另外一個用來測試UI的斷言類,這些都在
Assertion classes中由詳細的描述。
你可以閱讀junit.org首頁的文檔來學習更多的JUnit內容。注意android測試API支援JUnit 3的代碼風格,不支援JUnit 4。同時,你必須使用android的test runnerInstrumentationTestRunner來運行測試類別。這個test
runner在Running Tests部分有介紹。
Instrumentation
android instrumentation是android系統中一系列的控制方法或者鉤子(hooks)。這些hooks可以脫離組件的正常生命週期控制一個android組件。它同樣可以控制android如何載入應用程式。
通常情況下,一個android組件會按照系統指定的生命週期來運行,例如,一個Activity的生命週期開始於它被一個Intent啟用,它的onCreate()方法會被調用,接下來是onResume(),當使用者啟動另外一個應用程式,onPause()方法會被調用 ,如果Activity調用finish()方法,它的onDestroy()方法也會被調用。android framework API不會提供方法讓你在代碼中直接調用這些回調方法,但是你可以用instrumentation做到。
同時,系統將一個應用中的所有組件運行在同一個進程中,你可以讓一些組件,比如content provider,運行在一個單獨的進程中。但是你無法強制讓一個應用與另外一個正在啟動並執行應用程式運行在同一個進程中。
通過android imstrumentation,你可以在測試代碼中直接調用回調方法。它可以讓你一步一步地運行一個組件的生命週期,就像你正在調試這個組件。下面的測試代碼示範了如何用instrumentation來測試一個Activity儲存和恢複狀態:
// Start the main activity of the application under test mActivity = getActivity(); // Get a handle to the Activity object's main UI widget, a Spinner mSpinner = (Spinner)mActivity.findViewById(com.android.example.spinner.R.id.Spinner01); // Set the Spinner to a known position mActivity.setSpinnerPosition(TEST_STATE_DESTROY_POSITION); // Stop the activity - The onDestroy() method should save the state of the Spinner mActivity.finish(); // Re-start the Activity - the onResume() method should restore the state of the Spinner mActivity = getActivity(); // Get the Spinner's current position int currentPosition = mActivity.getSpinnerPosition(); // Assert that the current position is the same as the starting position assertEquals(TEST_STATE_DESTROY_POSITION, currentPosition);
代碼中使用的一個關鍵方法是getActivity(),它就是android instrumentation API中的內容。直到你調用該方法,需要測試的Activity才會被啟動。你可以提前配置測試所需的環境(test fixture),然後再調用該方法啟動Activity。
同時,instrumentation可以載入測試包和被測試應用程式到同一個進程中。因為應用程式的組件和測試都在同一個進程中,測試代碼可以調用應用組件的方法,修改和檢查組件中的屬性。
Test case 類
android提供了幾個繼承自TestCase和Assert的test case 類,它們都有andorid 特有的setup、teardown以及其它的輔助方法。
AndroidTestCase
一個通用的test case類,特別是你剛開始學習android測試,可以從AndroidTestCase開始。它繼承了TestCase和Assert類。它提供了標準的JUnit中的setup()和teardown()方法,同時還有JUnit的所有Assert方法。另外,它也提供了用來測試許可權的方法以及通過清除一定的類引用來防止記憶體泄露的方法。
組件特有的 test cases
android測試架構的一個重要特點即使組件專屬的 test cases 類。這些特有的組件測試需要提供測試前配置、測試後回收的方法控制組件生命週期的方法。同時它們 也提供建立類比對象的方法。這些類都會在組件特有測試的內容中講述:
- Activity Testing
- Content Provider Testing
- Service Testing
android並沒有為 BroadcastReceiver 提供一個單獨的 test case 類。可以通過測試發送Intent對象給它的組件來測試BroadcastReceiver,檢查BroadcastReceiver回複是否正確。
ApplicationTestCase
你可以用ApplicationTestCase這個 test case 類來測試Application對象的啟動和退出。這些對象維護著應用程式套件組合中所有組件資訊的全域狀態,這個test
case 用於驗證manifest 檔案中的<application>元素是否正確配置。然而記住這個test case 無法控制應用程式套件組件的測試。
InstrumentationTestCase
如果你想在一個test case 類中使用 instrumentation 的方法,你必須使用InstrumentationTestCase或者它的子類。Activity 的test case 繼承該基類,同時擴充了一些輔助Activity 測試的功能。
Assertion 類
因為android test case 類繼承自 JUnit,你可以用斷言來顯示測試結果。assertion 方法將測試返回的真實值和期望值進行比較,如果比較失敗它會拋出一個AssertionException。用Assertion比列印log 更方便,而且提供更好的測試效能。
除了JUnit的Assert類的方法,測試API同時也提供了MoreAsserts和ViewAsserts類:
MoreAsserts包含更多功能強大的斷言,例如進行Regex匹配的assertContainsRegex(String,
String)。
ViewAsserts包含很多關於View的斷言,例如它包含用來測試一個View是否在螢幕的特定的(X,Y)位置的assertHasScreenCoordinates(View,
View, int, int)方法,這些斷言簡化了UI中的結合和對準測試。
類比對象類
為瞭解決測試過程中的依賴,android提供了用來建立類比的系統對象的類,比如Context對象、ContentProvider對象、ContentResolver對象以及Service對象。有些
test case也提供類比的Intent對象。通過使用這些類比對象,你可以將測試與系統的其餘部分隔離開,同時也滿足了測試中的依賴,這些類都在包android.test和android.test.mock中。
類比對象通過不使用或者覆寫正常操作來實現將測試與正在啟動並執行系統隔離。例如,MockContentResolver對象用它自有的與系統隔離的本地架構來代替通常的resolver 架構。同時MockContentResolver不使用notifyChange(Uri,
ContentObserver, boolean)方法,這樣測試環境以外的observer對象不會被異常觸發。
類比對象類通過提供正常類的子類來滿足測試的依賴,該子類除了你覆寫的方法外其它都是不起作用的。例如,MockResources對象提供了Resources類的一個子類,其中每個方法在調用時都會拋出異常。要使用它,你只需要覆寫需要的方法。
下面是android中可用的類比對象類:
基本的類比對象類
MockApplication、MockContext、MockContentProvider、MockCursor,、MockDialogInterface、MockPackageManager和MockResources提供了一種簡單有用的類比策略。它們都是系統中對應的類的“方法不可用”的版本,它們的所有方法在調用時都會拋出UnsupportedOperationException異常。要使用它們,你需要覆寫用來滿足依賴所需要的方法。
注意:MockContentProvider和MockCursor是API Level 8
中新加入的API。
Resolver 類比對象
MockContentResolver通過屏蔽系統正常的resolver架構來為content provider 提供隔離的測試。MockContentResolver不是在系統中尋找提供authority的content provider,而是使用它自己的內部表,你必須顯式地用addProvider(String,
ContentProvider)方法將provider添加到表中。
通過這個特性,你可以將一個類比的content provider與一個authority關聯,建立一個provider對象使用測試資料,你甚至可以設定provider的authority為null。事實上,MockContentResolver對象將你的測試與包含真實資料的provider隔離。你可以控制provider的功能,也可以防止測試影響真實資料。
用來測試的Context
android提供了兩個Context類來提供測試:
IsolatedContext類提供一個隔離的Context,使用該Context的檔案、目錄和資料庫操作都會在一個單獨的測試地區。儘管功能有限,該類足以應對系統調用(this
Context has enough stub code to respond to system calls)。這個類允許你在不影響當前裝置上的真實資料的前提下測試應用的資料操作。
RenamingDelegatingContext提供了這樣一個Context,它的大部分功能都由一個現存的Context來處理,但是檔案和資料庫操作都由一個IsolatedContext來處理,隔離的部分使用一個測試目錄,並且建立特殊的檔案和目錄名,你可以自己控制命名,也可以讓constructor自動指定。該類為進行資料操作建立一個隔離地區提供了快捷辦法,同時不會影響Context其它的正常操作。
運行測試
test case 都是由一個test runner 類來運行,test runner 載入 test case 類、初始化、運行及清理每一項測試。android test runner 必須被註冊(must be instrumented),這樣啟動應用的系統功能可以控制測試包是如何載入test case和被測試包的。你可以通過在測試包的manifest檔案中設定一個值來告訴系統使用哪個註冊了的test runner。
InstrumentationTestRunner是android中主要的test runner類,它擴充了JUnit test runner架構並且是已經註冊的。它能夠執行所有由android系統提供的test case 類並且支援所有類型的測試。
你可以指定測試包的manifest檔案的<instrumentation>標籤內容為Instrumentation 或者它的子類。InstrumentationTestRunner的代碼在共用庫android.test.runner中,所以它通常沒有連結到你的代碼,你必須在一個<uses-library>標籤中指定它才可以。你不知道自己手動去設定這些標籤,帶有ADT的eclipse以及android
命令列工具都會自動產生它們並且把它們加到測試包的manifest檔案中。
注意:如果你使用的是InstrumentationTestRunner之外的test runner,你必須手動修改<instrumentation>標籤並指向你想使用的類。
要運行InstrumentationTestRunner類必須用android 工具調用內部隱藏的系統類別。當你用帶有ADT的eclipse執行測試的時候,這些類都會被自動調用,當你用命令列工具執行測試的時候,可以用Android
Debug Bridge (adb)運行這些類。
系統類別載入和啟動一個測試包,殺掉被測試應用程式套件正在啟動並執行進程,並且重新載入一個被測試包的實體,然後它們把控制權交給InstrumentationTestRunner,由它來執行測試包中的每個test case。你也可以通過eclipse中的setting或者命令列工具中的flag來控制哪些
test case或者方法在運行。
既不是系統類別也不是InstrumentationTestRunner運行被測試包,這是由test case來做的。它要麼調用被測試包中的方法,要麼調用它自己的可以改變被測試包生命週期的方法。應用程式完全由test case 控制,在一項測試開始前由test case來初始化測試環境,這在前面的測試展示一個Spinner的Activity的code
snippet中有示範。
關於更多的運行測試,可以參見 Testing from Eclipse with ADT和 Testing from Other IDEs。
查看測試結果
android 測試架構返回測試結果給啟動測試的工具。如果你是在帶有ADT的eclipse中運行測試,結果會在一個新的JUnit視圖面板中顯示,如果你從命令列啟動測試,結果會在STDOUT中顯示。在任何一種情況下,你都可以看到一份顯示每個test case 名字和你所啟動並執行方法的簡要總結,你同時會看到所有失敗的斷言,其中包含指向產生失敗的測試代碼所在行的連結。失敗的斷言同時也會列出期望值和實際值。
測試結果根據你所使用的IDE不同而有不同的格式。帶有ADT的eclipse的測試結果格式在 Testing from Eclipse with ADT中有描述,從命令列中開始啟動並執行測試結果格式在Testing
from Other IDEs部分有描述。
monkey 和 monkeyrunner
SDK提供了兩個非常實用的應用測試載入器:
- UI/Application Exerciser Monkey,通常稱為"monkey",它是一個向裝置發送偽隨機事件流(如擊鍵、觸控、手勢)的命令列工具。你可以通過Android Debug Bridge (adb)來運行它,對應用程式進行壓力測試然後報告遇到的錯誤。你可以通過每次使用相同的隨機數種子來運行它以重複事件流。
- monkeyrunner是一套API,也是用Python編寫的測試程式的運行環境。該API包含如下功能:串連到一台裝置、安裝和卸載軟體包、、比較兩張圖片(comparing two images)、運行應用程式對應的測試應用。通過該API,你可以寫出功能強大複雜的測試。使用該API 的程式可以通過命令列工具monkeyrunner來運行。
處理包名
在整個測試環境中,你需要同時處理android應用程式套件名和java 包標示符。它們都使用同樣的命名格式,但是代表著完全不同的實體,為了正確啟動測試你需要知道它們之間的區別。
android包名是.apk檔案對應的一個獨一無二的系統名字,由應用程式套件的manifest檔案中<manifest>標籤中的"android:package"屬性來設定。測試包的名字必須和被測試包的名字不同,通常android工具會用被測試包的名字後加上".test"來作為測試包的名字。
測試包也會用包名來定位它所測試的應用,由測試包的manifest檔案中<instrumentation>元素的"android:targetPackage"屬性設定。
一個java包標示符對應一個源檔案,包名反映了源檔案所在目錄,它同時會影響類與成員間彼此的可訪問性。
建立測試專案的android 工具會協助你設定一個測試包的名字。根據你的輸入,工具會設定測試包的名字以及測試的目標包的名字。只有在被測試應用工程已經存在的情況下這些工具才會起作用。
預設情況下,這些工具會將測試類別的包標示符設定為與被測試應用的包標示符一致。如果你想暴露被測試包中的一些成員你可能需要做一些修改。如果要修改,只修改java 包標示符,不要修改android 包名,只修改test case 的源檔案而不要修改測試包中R.java檔案的包名,因為修改它會造成與被測試包中的R.java類衝突。不要將測試包的android包名修改成和它所測試的應用的包名一樣,因為這樣它們的名字在系統中不再是獨一無二的。
測試什麼
What To Test詳細地描述了一個android應用中應該被測試的關鍵功能以及可能會影響該功能的狀況。
大部分的單元測試是專門針對你正在測試的andorid組件。Activity Testing、 Content Provider Testing和
Service Testing中都有一章節列出“需要測試什麼”。
可以的話,你應該在一台真實的裝置上運行這些測試。不行的話,你可以使用Android Emulator來載入已經配置好你所希望測試的硬體、螢幕、版本的android vitual device。
接下來
要學習如何在eclipse中配置和運行測試,請參考Testing from Eclipse with ADT,如果你不是用eclipse開發,請參考Testing from Other IDEs。
如果你想有一份關於android測試一步一步地介紹,請參考Activity Testing Tutorial。