自檢測測試範例

當儀器測試開始時,其目標套件將重新啟動,並注入儀器程式碼並啟動執行。一個例外是,這裡的目標套件不能是 Android 應用程式框架本身,例如套件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下建立一個新的子目錄。

無論結構如何,您最終都會使用類似於範例 gerrit 變更中的instrumentation目錄中的檔案來填入tests目錄或新建立的子目錄。每個文件的詳細資訊將在本文檔後面進行解釋。

清單文件

與應用程式專案一樣,每個儀器測試模組都需要一個名為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 應用程式框架用來識別應用程式(或在此上下文中:您的測試應用程式)的唯一識別碼。系統中的每個使用者只能安裝一個具有該套件名稱的應用程式。

此外,此package屬性與ComponentName#getPackageName()傳回的內容相同,也與使用adb shell與各種pm子命令互動時使用的屬性相同。

請注意,雖然套件名稱通常與 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 的藍圖檔案選項就足夠了。詳情請參閱簡單測試配置

複雜的設定檔

對於這些更複雜的情況,您還需要為 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

對於需要大量客製化的更複雜的情況,請遵循儀器說明