自我檢測測試範例

啟動檢測測試時,系統會重新啟動目標套件,並注入檢測程式碼,然後啟動執行作業。但有一個例外,就是這裡的目標套件不能是 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 套件名稱的關聯性很小。換句話說,您的應用程式 (或測試) 套件可能包含任何套件名稱的類別,但另一方面,您也可以選擇簡化,讓應用程式或測試中的頂層 Java 套件名稱與應用程式套件名稱相同。

android:sharedUserId="android.uid.system"

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

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

這是所有 Instrumentation 測試的必要條件,因為相關類別會封裝在個別的架構 JAR 程式庫檔案中,因此當應用程式架構叫用測試套件時,需要額外的類別路徑項目。

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

您可能已經發現,這裡的 targetPackage 宣告方式與這個檔案 manifest 標記中宣告的 package 屬性相同。如測試基礎知識所述,這類檢測設備測試通常用於測試架構 API,因此除了自身以外,指定目標應用程式套件對這類測試沒有太大意義。

簡單的設定檔

每個新測試模組都必須有設定檔,才能使用模組中繼資料、編譯時間依附元件和封裝指示,引導建構系統。在大多數情況下,Soong 型 Blueprint 檔案選項就已足夠。詳情請參閱「簡易測試設定」。

複雜的設定檔

在這些較複雜的情況下,您也需要為 Android 的測試架構 Trade Federation 編寫測試設定檔。

測試設定可以指定特殊裝置設定選項和預設引數,以提供測試類別。請參閱「/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

如需更複雜的案例,需要進行更深入的自訂,請按照儀表板操作說明操作。