Android Unit Test學習

Android Unit Test學習

@(單元測試)[Android|Markdown]

  • 單元測試 這幾天接觸了下android的單元測試,寫了一些小DEMO,自己總結下來,覺得android的單元測試可以有以下優缺點:
  • 優點
  1. 減少bug率
  2. 明確方法的輸入輸出
  3. 自測程序,使自己對代碼更有自信
  • 缺點
  1. 耗時
  2. 有些時候測試環境于真實環境有差異,導致做了很多適配的工作。
  3. 不適合一個硬性指標。如代碼覆蓋率等。

[TOC]

Android Test 框架

流行的android測試框架很多,可以參照這個網頁:Android 測試工具。我主要調研過以下幾種:

  1. 原生框架 :ActivityInstrumentationTestCase2
  2. Android Test Lib (谷歌官方提供)Espresso等
  3. Appium (可以編寫測試腳本)
  4. Monkey 和Monkey Runner(隨機測試,谷歌官方提供)
  5. Robolectric (第三方測試框架,不需要真機或者模擬器)
  6. Robotium (封裝官方框架)

上述幾種也可以組合使用,基本上想對于一個項目做完整的單元測試,即包括UI,業務,數據,協議等,一般都要自己再<font color=red>封裝一些適合項目的框架和工具類</font>。

原生框架

先上圖~


  • AndroidTestCase類:
    提供系統對象(如Context)的方法。使用Context,你可以瀏覽資源,文件,數據庫等等。不能測試涉及UI的方法。
  • InstrumentationTestCase類:
    繼承TestCase類,并可以使用Instrumentation框架,用于測試Activity。使用Instrumentation,Android可以向程序發送事件進行自動UI測試,并可以精確控制Activity的啟動,監測Activity生命周期的狀態
    ActivityUnitTestCase設計用于單元測試,它在一個孤立的系統環境中測試Activity。換句話說,當你使用這個測試類時,Activity不能與其它Activity交互。每個測試用例都需要使用startActivity啟動測試activity。

單元測試在Studio中的應用

Android Studio對單元測試完全支持,已經幫開發者搭建好了測試環境。測試開發者只需要關心測試用例編寫,不用關心測試環境搭建,給測試開發帶來極大便利。在Android Studio上進行JUnit單元測試步驟:
1 編寫測試用例。
編寫測試用例需要繼承TestCase的子類。如果測試對象為普通java類,自定義測試用例需要繼承AndroidTestCase;如果測試對象為Activity,可以繼承ActivityUnitTestCase,也可以繼承ActivityInstrumentationTestCase2。
繼承ActivityUnitTestCase和ActivityInstrumentationTestCase2不同之處在于,前者需要自己使用startActivity啟動被測試的activity,并且該activity在一個獨立的環境中運行。繼承自ActivityInstrumentationTestCase2的測試用例不需要開發者自己調用被測試的activity,測試框架會自動啟動activity。
自定義測試用例類需要有構造函數,并且將測試目標類傳遞給父類。如:
public LoadActivityTest() {

super(LoadActivity.class);//LoadActivity為測試目標類

}

一般來說自定義測試用例需要重寫setUp和tearDown,如果測試用例中有多個測試方法,setUp和tearDown會在每個測試方法前后被調用。setUp用來初始化測試環境,tearDown在每個測試方法結束后還原測試環境。
使用斷言assertXXX來判斷測試結果。如果斷言失敗,則測試用例測試不通過,所有斷言都成功,則測試用例測試通過。 常見的斷言有:
assertNotNull:斷言測試對象不為NULL;
assertNull:斷言測試對象為NULL;
assertEquals:斷言測試的兩個對象值相等;
assertTrue:斷言Boolean值為true;
assertFalse:斷言Boolean值為false。
所有測試方法必須以test開頭,如testXXX。測試框架會自動調用所有以test為開頭的測試方法。各個測試方法沒有運行先后順序。因此,各個測試方法不應該有前后依賴順序。

UI測試。

1)UI測試應該在主線程中操作。
activity.runOnUiThread(new Runnable() {

@Override

public void run() {

    mPaymentButton.callOnClick();

}

});

2)模擬系統發送按鍵事件
this.sendKeys(KeyEvent.KEYCODE_HOME);

3)點擊按鍵事件
mPaymentButton.callOnClick();

3 線程同步問題
1)測試線程和異步任務線程同步
在單元測試中,有很多異步任務。我們需要異步任務返回結果再測試,這就需要用到線程同步相關知識。比如,我們測試的單元需要發送一個網絡請求,需要測試返回結果是否正確。這是一個典型的異步任務,這就需要用本節的方法去做單元測試。
CountDownLatch,一個同步輔助類,用信號量機制實現線程同步。主要方法有:
public CountDownLatch(int count);
構造函數,count指定了計數器的次數??梢岳斫鉃榧拥逆i數量。
public void await() throws InterruptedException
調用此方法會一直阻塞當前線程,直到計數器的值為0。
public void countDown();
每次調用計數減少1,當計數為0時,線程被喚醒。
2)測試線程和UI主線程同步
每個測試方法都運行在子線程中,在某些情況,測試子線程需要等待主線程(UI線程)空閑才繼續運行測試子線程。這就需要同步測試子線程和UI主線程。Instrumentation為我們提供了waitForIdleSync方法來滿足這類同步需求。使用方法如下:

public void testExample(){

......

getInstrumentation().waitForIdleSync();//等待主線程返回
 .....

}

需要注意:waitForIdleSync只能在測試子線程中被調用。測試方法默認運行在子線程中,但也可以運行在UI線程中,只需要在測試方法前加上標注@UiThreadTest,這樣整個測試方法都在UI線程中運行。

測試Fragment

由于JUnit沒有提供對fragment測試的框架,所以只能在使用activity測試框架。通常的做法是使用ActivityInstrumentationTestCase2測試框架,先加載一個activity,然后在setup中啟動被測試的fragment。這樣在每個測試方法執行前都會啟動被測試的fragment。
使用ActivityInstrumentationTestCase2框架對fragment進行單元測試的不足之處在于,測試任何一個單元(函數),都需要先啟動該fragment,然后才能進行單元測試。

五 單元測試基礎要點
1 方法名稱必須以test開頭。
2不能依賴測試方法順序,每個測試方法都是在子線程中運行。
3 setUp方法和tearDown方法都是TestCase類的方法
1)setUp方法是在執行每個測試方法之前執行的
2)tearDown方法是在執行每個測試方法之后執行的
4 涉及UI操作的應該在主線程中執行。
5 waitForIdleSync和sendKeys不允許在UI線程里運行,只能運行在測試子線程中。

原理

需要說明的是,在Android系統中,測試程序也是應用程序,我們可以將其看成一個沒有UI的應用。
其實現過程大致如下:如圖,InstrumentationTestRunner通過調用Instrumentation殺除應用程序的進程,再用Instrumentation重啟該應用。這時,測試應用和被測應用就運行在同一進程下。測試應用怎么知道該測試哪個應用呢?嗯,這是通過在測試工程的mainfest文件中添加元素來實現的。當測試應用和被測應用運行在同一個進程里,它們之間就可以通過Instrumentation來進行消息交互,從而達到測試效果。當Instrumentation與某個程序交互時,其大致采用如下步驟:(資料來源:
http://blog.csdn.net/fireworkburn/article/details/20144153)。
首先,啟動時,初始化測試APK的配置文件AndroidManifest.xml文件中。該配置文件中標明了所使用的測試運行類、被測目標應用、包名等。然后,啟動被測應用的Activity。同時,將測試ActivityThread做為一個引用進行初始化。此時,如果找不到目標應用則會報錯。其次,執行測試腳本。測試時,測試工程中任何對目標應用進行的操作,都會用異步的方式,將消息體放在目標程序的MessageQueue中。這樣,目標程序在查看到自己的MessageQueue中有內容時就會執行。

Robolectric

根據我的研究以及我個人的需求,我覺得<font color=red>Robolectric</font>更適合開發使用,因為其速度快,而且不需要在真機上看效果,這樣可以把代碼邏輯都寫好,驗證完畢在真機上一次運行。大大提高代碼質量和效率。個人見解,不喜勿噴。

Robolectric簡介

robolectric官網介紹,Robolectric是為android上的TDD開發而產生的一款第三方測試框架,按照官網說的:

Robolectric is a unit test framework that de-fangs the Android SDK jar so you can test-drive the development of your Android app. Tests run inside the JVM on your workstation in seconds. With Robolectric you can write tests like this:

@RunWith(RobolectricTestRunner.class)
public class MyActivityTest {

  @Test
  public void clickingButton_shouldChangeResultsViewText() throws Exception {
    MyActivity activity = Robolectric.setupActivity(MyActivity.class);

    Button button = (Button) activity.findViewById(R.id.button);
    TextView results = (TextView) activity.findViewById(R.id.results);

    button.performClick();
    assertThat(results.getText().toString()).isEqualTo("Robolectric Rocks!");
  }
}

該框架測試起來是很方便的,但是同時由于不啟動app,可能遇到一些問題,總體上使用起來還是方便的。

eclipse配置

新建 Test Project

這里我新建了一個 Maven Project

File> New > Other > Maven Project ,跟 Appium 里面使用 Java 寫腳本,建立 Maven 項目是相同的。

我建立的測試項目 roboletrictest,pom.xml 的內容我這里貼下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>roboletrictest</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <name>roboletrictest</name>
  <url>http://maven.apache.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.robolectric</groupId>
      <artifactId>robolectric</artifactId>
      <version>3.0</version>
      <scope>test</scope>
    </dependency>

  </dependencies>
</project>

配置 Test Project

配置 roboletrictest 的 Build Path

右鍵項目(roboletrictest) > Build Path > Configure Build Path
在 Project 選項中 add 被測項目(roboletric)
在 Libraries 中 Add External JARs,添加對應的 android.jar,android.jar 在 sdk 中(需要下載)
完成 Test Case

在 src/test/java 目錄下 new 一個 package,對應被測項目(com.example.roboletric.test)
在包里面 new 一個 JUnit Test Case,使用 Junit 4,Class Name 為 MainActivityTest
Run Test Case

右鍵 MainActivityTest > Run As > JUnit Test

自己打包依賴

因為是maven項目,其實你也可以自己把roboletric的項目打包,使用maven-assembly-plugin把項目打成jar包,直接導入使用。以前的官網提供了依賴包的,但是現在找不著了。

Android studio配置

 repositories {
        maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
        mavenLocal()
        mavenCentral()
    }
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.0.0'
    testCompile "junit:junit:4.10"
    testCompile "org.assertj:assertj-core:1.7.0"
    testCompile "org.robolectric:robolectric:${robolectricVersion}"
}

Shadow類的使用

Android Test Kit

Android Test Kit 是一組 Google 開源測試工具,用于 Android 平臺,包含 Espresso API 可用于編寫簡潔可靠的 Android UI 測試。它也可以跟其他的框架混合使用。
github Demo的地址:
https://github.com/googlesamples/android-testing

Monkey 和MonkeyRunner

Appium

框架基本原理

下次再看,我會開一篇代理設計模式的專欄

常見的測試方法

測試UI

一般的測試框架都提供了對UI的測試,具體可以參考谷歌源碼對一些原生app做的測試。

測試業務

建議把業務方法的入口都放在manager里,這樣測試的時候直接一調用就OK了。但是記住要測試一些邊界情況,例如空指針之類的。

測試http請求

因為http請求是異步的,這塊可能要對請求的框架做一些封裝,以便支持mock數據。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android單元測試介紹 處于高速迭代開發中的Android項目往往需要除黑盒測試外更加可靠的質量保障,這正是單...
    東經315度閱讀 3,153評論 6 37
  • 標簽(空格分隔): Android 單元測試的好處:Martin Fowler在《重構》里面還解釋了為什么單元測試...
    背影殺手不太冷閱讀 5,847評論 3 25
  • 一.基本介紹 背景: 目前處于高速迭代開發中的Android項目往往需要除黑盒測試外更加可靠的質量保障,這正是單元...
    anmi7閱讀 2,059評論 0 6
  • @Author:彭海波 前言 單元測試(又稱為模塊測試, Unit Testing)是針對程序模塊(軟件設計的最小...
    海波筆記閱讀 5,014評論 0 52
  • 前言 在博客Android單元測試之PowerMockito,主要介紹PowerMockito的使用和對Java測...
    水木飛雪閱讀 11,101評論 14 15