> 編寫:[huanglizhuo](https://github.com/huanglizhuo) - 原文:[http://developer.android.com/training/](http://developer.android.com/training/activity-testing/activity-basic-testing.html)[activity](# "An activity represents a single screen with a user interface.")-testing/[activity](# "An activity represents a single screen with a user interface.")-basic-testing.html
為了驗證應用的布局設計和功能是否符合預期,為應用的每個[Activity](# "An activity represents a single screen with a user interface.")建立測試非常重要。對于每一個測試,我們需要在測試用例中創建一個個獨立的部分,包括測試數據,前提條件和[Activity](# "An activity represents a single screen with a user interface.")的測試方法。之后我們就可以運行測試并得到測試報告。如果有任何測試沒有通過,這表明在我們代碼中可能有潛在的缺陷。
> **注意**: 在測試驅動開發(TDD)方法中, 不推薦先編寫大部分或整個應用,并在開發完成后再運行測試。而是應該先編寫測試,然后及時編寫正確的代碼,以通過測試。通過更新測試案例來反映新的功能需求,并以此反復。
### 創建一個測試用例
[Activity](# "An activity represents a single screen with a user interface.")測試都是通過結構化的方式編寫的。請務必把測試代碼放在一個單獨的包內,從而與被測試的代碼分開。
按照慣例,測試包的名稱應該遵循與應用包名相同的命名方式,在應用包名后接“.tests”。在創建的測試包中,為我們的測試用例添加Java類。按照慣例,測試用例名稱也應遵循要測試的Java或Android的類相同的名稱,并增加后綴“Test”。
要在Eclipse中創建一個新的測試用例可遵循如下步驟:
a. 在Package Explorer中,右鍵點擊待測試工程的src/文件夾,**New > Package**。
b. 設置文件夾名稱`<你的包名稱>.tests`(比如, `com.example.android.testingfun.tests`)并點擊**Finish**。
c. 右鍵點擊創建的測試包,并選擇**New > Calss**。
d. 設置文件名稱`<你的Activity名稱>Test`(比如, `MyFirstTestActivityTest`),然后點擊**Finish**。
### 建立測試數據集(Fixture)
測試數據集包含運行測試前必須生成的一些對象。要建立測試數據集,可以在我們的測試中覆寫[setUp()](http://developer.android.com/reference/junit/framework/TestCase.html#setUp())和[tearDown()](http://developer.android.com/reference/junit/framework/TestCase.html#tearDown())方法。測試會在運行任何其它測試方法之前自動執行[setUp()](http://developer.android.com/reference/junit/framework/TestCase.html#setUp())方法。我們可以用這些方法使得被測試代碼與測試初始化和清理是分開的。
在你的Eclipse中建立測試數據集:
1 . 在 Package Explorer中雙擊測試打開之前編寫的測試用例,然后修改測試用例使它繼承[ActivityTestCase](http://developer.android.com/reference/android/test/ActivityTestCase.html)的子類。比如:
~~~
public class MyFirstTestActivityTest
extends ActivityInstrumentationTestCase2<MyFirstTestActivity> {
~~~
2 . 下一步,給測試用例添加構造函數和setUp()方法,并為我們想測試的[Activity](# "An activity represents a single screen with a user interface.")添加變量聲明。比如:
~~~
public class MyFirstTestActivityTest
extends ActivityInstrumentationTestCase2<MyFirstTestActivity> {
private MyFirstTestActivity mFirstTestActivity;
private TextView mFirstTestText;
public MyFirstTestActivityTest() {
super(MyFirstTestActivity.class);
}
@Override
protected void setUp() throws Exception {
super.setUp();
mFirstTestActivity = getActivity();
mFirstTestText =
(TextView) mFirstTestActivity
.findViewById(R.id.my_first_test_text_view);
}
}
~~~
構造函數是由測試用的Runner調用,用于初始化測試類的,而[setUp()](http://developer.android.com/reference/junit/framework/TestCase.html#setUp())方法是由測試Runner在其他測試方法開始前運行的。
通常在`setUp()`方法中,我們應該:
- 為`setUp()` 調用父類構造函數,這是JUnit要求的。
- 初始化測試數據集的狀態,具體而言:
- 定義保存測試數據及狀態的實例變量
- 創建并保存正在測試的[Activity](# "An activity represents a single screen with a user interface.")的引用實例。
- 獲得想要測試的[Activity](# "An activity represents a single screen with a user interface.")中任何UI組件的引用。
我們可以使用[getActivity()](http://developer.android.com/reference/android/test/ActivityInstrumentationTestCase2.html#getActivity())方法得到正在測試的[Activity](# "An activity represents a single screen with a user interface.")的引用。
### 增加一個測試前提
我們最好在執行測試之前,檢查測試數據集的設置是否正確,以及我們想要測試的對象是否已經正確地初始化。這樣,測試就不會因為有測試數據集的設置錯誤而失敗。按照慣例,驗證測試數據集的方法被稱為`testPreconditions()`。
例如,我們可能想添加一個像這樣的`testPreconditons()`方法:
~~~
public void testPreconditions() {
assertNotNull(“mFirstTestActivity is null”, mFirstTestActivity);
assertNotNull(“mFirstTestText is null”, mFirstTestText);
}
~~~
Assertion(斷言,譯者注)方法源自于Junit[Assert](http://developer.android.com/reference/junit/framework/Assert.html)類。通常,我們可以使用斷言來驗證某一特定的條件是否是真的。
- 如果條件為假,斷言方法拋出一個AssertionFailedError異常,通常會由測試Runner報告。我們可以在斷言失敗時給斷言方法添加一個字符串作為第一個參數從而給出一些上下文詳細信息。
- 如果條件為真,測試通過。
在這兩種情況下,Runner都會繼續運行其它測試用例的測試方法。
### 添加一個測試方法來驗證[Activity](# "An activity represents a single screen with a user interface.")
下一步,添加一個或多個測試方法來驗證[Activity](# "An activity represents a single screen with a user interface.")布局和功能。
例如,如果我們的[Activity](# "An activity represents a single screen with a user interface.")含有一個[TextView](http://developer.android.com/reference/android/widget/TextView.html),可以添加如下方法來檢查它是否有正確的標簽文本:
~~~
public void testMyFirstTestTextView_labelText() {
final String expected =
mFirstTestActivity.getString(R.string.my_first_test);
final String actual = mFirstTestText.getText().toString();
assertEquals(expected, actual);
}
~~~
該 `testMyFirstTestTextView_labelText()` 方法只是簡單的檢查Layout中[TextView](http://developer.android.com/reference/android/widget/TextView.html)的默認文本是否和`strings.xml`資源中定義的文本一樣。
> **注意**:當命名測試方法時,我們可以使用下劃線將被測試的內容與測試用例區分開。這種風格使得我們可以更容易分清哪些是測試用例。
做這種類型的字符串比較時,推薦從資源文件中讀取預期字符串,而不是在代碼中硬性編寫字符串做比較。這可以防止當資源文件中的字符串定義被修改時,會影響到測試的效果。
為了進行比較,預期的和實際的字符串都要做為[assertEquals()](http://developer.android.com/reference/junit/framework/Assert.html#assertEquals(java.lang.String, java.lang.String))方法的參數。如果值是不一樣的,斷言將拋出一個[AssertionFailedError](http://developer.android.com/reference/junit/framework/AssertionFailedError.html)異常。
如果添加了一個`testPreconditions()`方法,我們可以把測試方法放在testPreconditions之后。
要參看一個完整的測試案例,可以參考本節示例中的MyFirstTestActivityTest.java。
### 構建和運行測試
我們可以在Eclipse中的包瀏覽器(Package Explorer)中運行我們的測試。
利用如下步驟構建和運行測試:
1.
連接一個Android設備,在設備或模擬器中,打開設置菜單,選擇開發者選項并確保啟用USB調試。
1.
在包瀏覽器(Package Explorer)中,右鍵單擊測試類,并選擇**Run As > Android Junit Test**。
1.
在Android設備選擇對話框,選擇剛才連接的設備,然后單擊“確定”。
1.
在JUnit視圖,驗證測試是否通過,有無錯誤或失敗。
本節示例代碼[AndroidTestingFun.zip](http://developer.android.com/shareables/training/AndroidTestingFun.zip)
- 序言
- Android入門基礎:從這里開始
- 建立第一個App
- 創建Android項目
- 執行Android程序
- 建立簡單的用戶界面
- 啟動其他的Activity
- 添加ActionBar
- 建立ActionBar
- 添加Action按鈕
- 自定義ActionBar的風格
- ActionBar的覆蓋層疊
- 兼容不同的設備
- 適配不同的語言
- 適配不同的屏幕
- 適配不同的系統版本
- 管理Activity的生命周期
- 啟動與銷毀Activity
- 暫停與恢復Activity
- 停止與重啟Activity
- 重新創建Activity
- 使用Fragment建立動態的UI
- 創建一個Fragment
- 建立靈活動態的UI
- Fragments之間的交互
- 數據保存
- 保存到Preference
- 保存到文件
- 保存到數據庫
- 與其他應用的交互
- Intent的發送
- 接收Activity返回的結果
- Intent過濾
- Android分享操作
- 分享簡單的數據
- 給其他App發送簡單的數據
- 接收從其他App返回的數據
- 給ActionBar增加分享功能
- 分享文件
- 建立文件分享
- 分享文件
- 請求分享一個文件
- 獲取文件信息
- 使用NFC分享文件
- 發送文件給其他設備
- 接收其他設備的文件
- Android多媒體
- 管理音頻播放
- 控制音量與音頻播放
- 管理音頻焦點
- 兼容音頻輸出設備
- 拍照
- 簡單的拍照
- 簡單的錄像
- 控制相機硬件
- 打印
- 打印照片
- 打印HTML文檔
- 打印自定義文檔
- Android圖像與動畫
- 高效顯示Bitmap
- 高效加載大圖
- 非UI線程處理Bitmap
- 緩存Bitmap
- 管理Bitmap的內存
- 在UI上顯示Bitmap
- 使用OpenGL ES顯示圖像
- 建立OpenGL ES的環境
- 定義Shapes
- 繪制Shapes
- 運用投影與相機視圖
- 添加移動
- 響應觸摸事件
- 添加動畫
- View間漸變
- 使用ViewPager實現屏幕側滑
- 展示卡片翻轉動畫
- 縮放View
- 布局變更動畫
- Android網絡連接與云服務
- 無線連接設備
- 使得網絡服務可發現
- 使用WiFi建立P2P連接
- 使用WiFi P2P服務
- 執行網絡操作
- 連接到網絡
- 管理網絡
- 解析XML數據
- 高效下載
- 為網絡訪問更加高效而優化下載
- 最小化更新操作的影響
- 避免下載多余的數據
- 根據網絡類型改變下載模式
- 云同步
- 使用備份API
- 使用Google Cloud Messaging
- 解決云同步的保存沖突
- 使用Sync Adapter傳輸數據
- 創建Stub授權器
- 創建Stub Content Provider
- 創建Sync Adpater
- 執行Sync Adpater
- 使用Volley執行網絡數據傳輸
- 發送簡單的網絡請求
- 建立請求隊列
- 創建標準的網絡請求
- 實現自定義的網絡請求
- Android聯系人與位置信息
- Android聯系人信息
- 獲取聯系人列表
- 獲取聯系人詳情
- 使用Intents修改聯系人信息
- 顯示聯系人頭像
- Android位置信息
- 獲取最后可知位置
- 獲取位置更新
- 顯示位置地址
- 創建和監視地理圍欄
- Android可穿戴應用
- 賦予Notification可穿戴特性
- 創建Notification
- 在Notifcation中接收語音輸入
- 為Notification添加顯示頁面
- 以Stack的方式顯示Notifications
- 創建可穿戴的應用
- 創建并運行可穿戴應用
- 創建自定義的布局
- 添加語音功能
- 打包可穿戴應用
- 通過藍牙進行調試
- 創建自定義的UI
- 定義Layouts
- 創建Cards
- 創建Lists
- 創建2D-Picker
- 創建確認界面
- 退出全屏的Activity
- 發送并同步數據
- 訪問可穿戴數據層
- 同步數據單元
- 傳輸資源
- 發送與接收消息
- 處理數據層的事件
- Android TV應用
- 創建TV應用
- 創建TV應用的第一步
- 處理TV硬件部分
- 創建TV的布局文件
- 創建TV的導航欄
- 創建TV播放應用
- 創建目錄瀏覽器
- 提供一個Card視圖
- 創建詳情頁
- 顯示正在播放卡片
- 幫助用戶在TV上探索內容
- TV上的推薦內容
- 使得TV App能夠被搜索
- 使用TV應用進行搜索
- 創建TV游戲應用
- 創建TV直播應用
- TV Apps Checklist
- Android企業級應用
- Ensuring Compatibility with Managed Profiles
- Implementing App Restrictions
- Building a Work Policy Controller
- Android交互設計
- 設計高效的導航
- 規劃屏幕界面與他們之間的關系
- 為多種大小的屏幕進行規劃
- 提供向下和橫向導航
- 提供向上和歷史導航
- 綜合:設計樣例 App
- 實現高效的導航
- 使用Tabs創建Swipe視圖
- 創建抽屜導航
- 提供向上的導航
- 提供向后的導航
- 實現向下的導航
- 通知提示用戶
- 建立Notification
- 當啟動Activity時保留導航
- 更新Notification
- 使用BigView風格
- 顯示Notification進度
- 增加搜索功能
- 建立搜索界面
- 保存并搜索數據
- 保持向下兼容
- 使得你的App內容可被Google搜索
- 為App內容開啟深度鏈接
- 為索引指定App內容
- Android界面設計
- 為多屏幕設計
- 兼容不同的屏幕大小
- 兼容不同的屏幕密度
- 實現可適應的UI
- 創建自定義View
- 創建自定義的View類
- 實現自定義View的繪制
- 使得View可交互
- 優化自定義View
- 創建向后兼容的UI
- 抽象新的APIs
- 代理至新的APIs
- 使用舊的APIs實現新API的效果
- 使用版本敏感的組件
- 實現輔助功能
- 開發輔助程序
- 開發輔助服務
- 管理系統UI
- 淡化系統Bar
- 隱藏系統Bar
- 隱藏導航Bar
- 全屏沉浸式應用
- 響應UI可見性的變化
- 創建使用Material Design的應用
- 開始使用Material Design
- 使用Material的主題
- 創建Lists與Cards
- 定義Shadows與Clipping視圖
- 使用Drawables
- 自定義動畫
- 維護兼容性
- Android用戶輸入
- 使用觸摸手勢
- 檢測常用的手勢
- 跟蹤手勢移動
- Scroll手勢動畫
- 處理多觸摸手勢
- 拖拽與縮放
- 管理ViewGroup中的觸摸事件
- 處理鍵盤輸入
- 指定輸入法類型
- 處理輸入法可見性
- 兼容鍵盤導航
- 處理按鍵動作
- 兼容游戲控制器
- 處理控制器輸入動作
- 支持不同的Android系統版本
- 支持多個控制器
- Android后臺任務
- 在IntentService中執行后臺任務
- 創建IntentService
- 發送工作任務到IntentService
- 報告后臺任務執行狀態
- 使用CursorLoader在后臺加載數據
- 使用CursorLoader執行查詢任務
- 處理查詢的結果
- 管理設備的喚醒狀態
- 保持設備的喚醒
- 制定重復定時的任務
- Android性能優化
- 管理應用的內存
- 代碼性能優化建議
- 提升Layout的性能
- 優化layout的層級
- 使用include標簽重用layouts
- 按需加載視圖
- 使得ListView滑動順暢
- 優化電池壽命
- 監測電量與充電狀態
- 判斷與監測Docking狀態
- 判斷與監測網絡連接狀態
- 根據需要操作Broadcast接受者
- 多線程操作
- 在一個線程中執行一段特定的代碼
- 為多線程創建線程池
- 啟動與停止線程池中的線程
- 與UI線程通信
- 避免出現程序無響應ANR
- JNI使用指南
- 優化多核處理器(SMP)下的Android程序
- Android安全與隱私
- Security Tips
- 使用HTTPS與SSL
- 為防止SSL漏洞而更新Security
- 使用設備管理條例增強安全性
- Android測試程序
- 測試你的Activity
- 建立測試環境
- 創建與執行測試用例
- 測試UI組件
- 創建單元測試
- 創建功能測試
- 術語表