自我檢測測試範例

啟動檢測設備測試時,系統會重新啟動目標套件,並注入檢測設備程式碼並啟動執行。其中一個例外狀況是,此處的目標套件不能是 Android 應用程式架構本身,例如套件 android,因為這會導致矛盾的情況,也就是需要重新啟動 Android 架構,而這會支援系統功能,包括測試工具本身。

這表示檢測設備測試無法將自己注入 Android 架構 (又稱為系統伺服器) 進行執行。為了測試 Android 架構,測試程式碼只能叫用公用 API 途徑,或是使用 Android 介面定義語言 AIDL 在平台來源樹狀結構中公開的途徑。就這種測試類別而言,指定任何特定套件都不是有意義的。因此,這類檢測設備通常會宣告為其專屬的測試應用程式套件指定目標,如 AndroidManifest.xml 專屬的 <manifest> 標記所定義。

視需求而定,這個類別的測試應用程式套件可能還會:

  • 測試所需的套件活動。
  • 將使用者 ID 提供給系統。
  • 使用平台金鑰簽署。
  • 根據架構來源而非公開 SDK 進行編譯。

這類檢測設備測試有時也稱為「自我檢測」。以下列舉平台來源中的一些自檢測測試範例:

本節的範例是撰寫新的檢測設備測試,並將目標套件設為其專屬的測試應用程式套件。本指南會以以下測試做為範例:

建議您先瀏覽程式碼,瞭解大概後再繼續進行。

決定來源位置

通常您的團隊已建立檢查程式碼的模式,以及新增測試的位置。大多數團隊都擁有單一 Git 存放區,或者與其他團隊共用,但會有包含元件原始碼的專屬子目錄。

假設元件來源的根位置位於 <component source root>,則大多數元件底下都有 srctests 資料夾,以及一些其他檔案,例如 Android.mk (或拆分成額外的 .mk 檔案)、資訊清單檔案 AndroidManifest.xml 和測試設定檔「AndroidTest.xml」。

由於您要新增全新的測試,因此可能需要在元件 src 旁邊建立 tests 目錄,並填入內容。

在某些情況下,團隊可能需要將不同測試套件封裝成個別的 APK,因此會在 tests 下建立更多目錄結構。在這種情況下,您必須在 tests 下建立新的子目錄。

無論結構為何,您最終都會在 tests 目錄或新建立的子目錄中填入檔案,這些檔案類似於範例 Gerrit 變更中的 instrumentation 目錄。本文後續會說明各個檔案的詳細資訊。

資訊清單檔案

與應用程式專案一樣,每個檢測工具測試模組都需要一個名為 AndroidManifest.xml 的資訊清單檔案。如要使用 BUILD_PACKAGE 核心 makefile 自動納入這個檔案,請在測試模組的 Android.mk 檔案旁邊提供這個檔案。

如果您不熟悉 AndroidManifest.xml 檔案,請參閱「應用程式資訊清單總覽

以下是 AndroidManifest.xml 檔案範例:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  android:sharedUserId="android.uid.system"
  package="android.test.example.helloworld" >

    <application>
       <uses-library android:name="android.test.runner"/>
    </application>

    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
                     android:targetPackage="android.test.example.helloworld"
                     android:label="Hello World Test"/>

</manifest>

資訊清單檔案的部分選取備註:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="android.test.example.helloworld" >

package 屬性是應用程式套件名稱:這是 Android 應用程式架構用來識別應用程式 (或在本例中:測試應用程式) 的專屬 ID。系統中的每位使用者只能安裝一個套件名稱相同的應用程式。

此外,這個 package 屬性與 ComponentName#getPackageName() 傳回的內容相同,同樣地,用來與各種 pm 子指令互動的指令也是使用 adb shell

請注意,雖然套件名稱通常與 Java 套件名稱採用相同的樣式,但實際上兩者之間的關聯性很低。換句話說,應用程式 (或測試) 套件可包含任何套件名稱的類別,但您也可以選擇簡化方式,讓應用程式或測試中的頂層 Java 套件名稱與應用程式套件名稱相同。

android:sharedUserId="android.uid.system"

這會宣告在安裝期間,這個 APK 檔案應獲得與核心平台相同的使用者 ID (即執行階段身分)。請注意,這取決於 APK 是否使用與核心平台相同的憑證進行簽署 (請參閱上一節的 LOCAL_CERTIFICATE),但這兩者是不同的概念:

  • 部分權限或 API 受到簽章保護,因此需要相同的簽署憑證
  • 部分權限或 API 需要呼叫端的 system 使用者身分,因此呼叫端套件必須與 system 共用使用者 ID (如果是核心平台本身的獨立套件)
<uses-library android:name="android.test.runner" />

所有檢測設備測試都必須這麼做,因為相關類別均封裝在獨立的架構 JAR 程式庫檔案中,因此在應用程式架構叫用測試套件時,需要額外的類別路徑項目。

android:targetPackage="android.test.example.helloworld"

您可能已經注意到,此處宣告的 targetPackage 與此檔案 manifest 標記中宣告的 package 屬性相同。如測試基本概念所述,這類檢測設備測試通常用於測試架構 API,因此除了自身之外,不太適合指定特定應用程式套件。

簡易設定檔

每個新測試模組都必須有設定檔,以便透過模組中繼資料、編譯時間依附元件和封裝指示,引導建構系統。在大多數情況下,以 Soong 為基礎的藍圖檔案選項就足夠了。詳情請參閱「簡易測試設定」。

複雜的設定檔

如果是較複雜的情況,您也需要為 Android 測試控管工具「貿易聯盟」撰寫測試設定檔。

測試設定可以指定特殊裝置設定選項和預設引數,以提供測試類別。請參閱 /platform_testing/tests/example/instrumentation/AndroidTest.xml 中的範例。

為了方便您操作,我們已隨附快照:

<configuration description="Runs sample instrumentation test.">
  <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
  <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
    <option name="test-file-name" value="HelloWorldTests.apk"/>
  </target_preparer>
  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
  <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
  <option name="test-suite-tag" value="apct"/>
  <option name="test-tag" value="SampleInstrumentationTest"/>

  <test class="com.android.tradefed.testtype.AndroidJUnitTest">
    <option name="package" value="android.test.example.helloworld"/>
    <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
  </test>
</configuration>

測試設定檔中的部分精選註解:

<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
  <option name="test-file-name" value="HelloWorldTests.apk"/>
</target_preparer>

這會告訴 Trade Federation 使用指定的 target_preparer 將 HelloWorldTests.apk 安裝到目標裝置上。Trade Federation 提供許多目標準備工具,可讓開發人員在執行測試前確保裝置設定正確。

<test class="com.android.tradefed.testtype.AndroidJUnitTest">
  <option name="package" value="android.test.example.helloworld"/>
  <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
</test>

這會指定要用於執行測試的 Trade Federation 測試類別,以及在要執行的裝置上套件中傳遞的測試執行工具架構,在本例中為 JUnit。

詳情請參閱「測試模組設定」。

JUnit4 功能

使用 android-support-test 程式庫做為測試執行程式,可採用新的 JUnit4 樣式測試類別,而範例 Gerrit 變更則包含一些非常基本功能的使用方式。請參閱 /platform_testing/tests/example/instrumentation/src/android/test/example/helloworld/HelloWorldTest.java 中的範例。

雖然測試模式通常是元件團隊專屬,但有些使用模式通常相當實用。

@RunWith(JUnit4.class)
public class HelloWorldTest {

JUnit4 的重大差異在於,測試不再需要繼承自通用基礎測試類別;而是在純 Java 類別中編寫測試,並使用註解指出特定測試設定和限制。在本例中,我們會指示這個類別應做為 JUnit4 測試執行。

    @BeforeClass
    public static void beforeClass() {
    ...
    @AfterClass
    public static void afterClass() {
    ...
    @Before
    public void before() {
    ...
    @After
    public void after() {
    ...
    @Test
    @SmallTest
    public void testHelloWorld() {
    ...

JUnit4 會在方法上使用 @Before@After 註解,執行測試前設定和測試後拆除作業。同樣地,JUnit4 會在方法上使用 @BeforeClass@AfterClass 註解,在執行測試類別中的所有測試之前執行設定,並在之後進行解構。請注意,類別範圍設定和拆除方法須為靜態。至於測試方法,與早期版本的 JUnit 不同,這些方法的名稱不再需要以 test 開頭,而是必須使用 @Test 註解。如常,測試方法必須是公開的,且不宣告傳回值、不接受參數,且可能擲回例外狀況。

檢測類別存取權

雖然基本 hello world 範例未涵蓋這點,但 Android 測試通常需要存取 Instrumentation 例項:這是核心 API 介面,可提供應用程式內容、活動生命週期相關測試 API 等的存取權。

由於 JUnit4 測試不再需要通用基礎類別,因此不再需要透過 InstrumentationTestCase#getInstrumentation() 取得 Instrumentation 例項,而是透過 InstrumentationRegistry 管理,其中儲存了檢測設備架構建立的內容和環境設定。

如要存取 Instrumentation 類別的例項,只要在 InstrumentationRegistry 類別上呼叫靜態方法 getInstrumentation() 即可:

Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()

在本機建構及測試

最常見的用途是使用 Atest

如需更複雜的做法,且需要更多自訂功能,請按照檢測器操作說明操作。