當儀器測試開始時,其目標包將重新啟動,並註入儀器代碼並啟動執行。一個例外是這裡的目標包不能是 Android 應用程序框架本身,即包android
,因為這樣做會導致 Android 框架需要重新啟動的矛盾情況,這是支持系統功能的,包括檢測本身。
這意味著插樁測試無法將自身注入到 Android 框架(即係統服務器)中執行。為了測試 Android 框架,測試代碼只能調用公共 API 表面,或通過平台源代碼樹中可用的 Android 接口定義語言AIDL公開的那些。對於此類測試,針對任何特定包沒有意義。因此,通常聲明此類檢測以其自己的測試應用程序包為目標,如其自己的AndroidManifest.xml
的<manifest>
標記中所定義。
根據要求,此類別中的測試應用程序包還可以:
- 捆綁測試所需的活動。
- 與系統共享用戶 ID。
- 使用平台密鑰進行簽名。
- 針對框架源而非公共 SDK 進行編譯。
此類儀器測試有時稱為自儀器測試。以下是平台源代碼中的一些自檢測示例:
此處涵蓋的示例是編寫一個新的儀器測試,目標包設置在其自己的測試應用程序包中。本指南使用以下測試作為示例:
建議先瀏覽一下代碼,在繼續之前獲得一個大概的印象。
確定源位置
通常,您的團隊已經建立了用於簽入代碼的位置和用於添加測試的位置的既定模式。大多數團隊擁有一個單獨的 git 存儲庫,或者與其他團隊共享一個,但有一個包含組件源代碼的專用子目錄。
假設您的組件源的根位置在<component source root>
,大多數組件下面都有src
和tests
文件夾,以及一些額外的文件,例如Android.mk
(或分解成額外的.mk
文件),清單文件AndroidManifest.xml
和測試配置文件“AndroidTest.xml”。
由於您正在添加一個全新的測試,您可能需要在組件src
旁邊創建tests
目錄,並用內容填充它。
在某些情況下,由於需要將不同的測試套件打包到單獨的 apk 中,您的團隊可能有更多的目錄結構正在tests
中。在這種情況下,您需要在tests
下創建一個新的子目錄。
無論結構如何,您最終都會使用與示例 gerrit 更改中的instrumentation
目錄中的文件類似的文件填充tests
目錄或新創建的子目錄。以下部分將詳細解釋每個文件。
清單文件
就像常規應用程序一樣,每個儀器測試模塊都需要一個清單文件。如果您將該文件命名為AndroidManifest.xml
並將其提供給您的測試模塊的Android.mk
旁邊,它將自動包含在BUILD_PACKAGE
核心 makefile 中。
在繼續之前,強烈建議先閱讀App Manifest Overview 。
這給出了清單文件的基本組件及其功能的概述。請參閱platform_testing/tests/example/instrumentation/AndroidManifest.xml中的示例。
為方便起見,此處包含快照:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.test.example.helloworld" >
<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
註釋來執行測試前設置和測試後拆卸。類似地,@ @AfterClass
@BeforeClass
被 JUnit4 用於方法,以在執行測試類中的所有測試之前執行設置,並在之後執行拆卸。請注意,類範圍的設置和拆卸方法必須是靜態的。至於測試方法,與早期版本的 JUnit 不同,它們不再需要以test
開頭的方法名稱,而是每個方法都必須使用@Test
註解。像往常一樣,測試方法必須是公共的,聲明沒有返回值,沒有參數,並且可能會拋出異常。
重要:測試方法本身用@Test
註釋進行註釋;並註意,對於通過 APCT 執行的測試,它們必須用測試大小進行註釋:示例註釋方法testHelloWorld
為@SmallTest
。註釋可以應用於方法範圍或類範圍。
訪問instrumentation
雖然基本的 hello world 示例中沒有涉及,但 Android 測試需要訪問Instrumentation
實例是相當普遍的:這是提供對應用程序上下文、活動生命週期相關測試 API 等的訪問的核心 API 接口。
因為 JUnit4 測試不再需要公共基類,所以不再需要通過InstrumentationTestCase#getInstrumentation()
獲取Instrumentation
實例,相反,新的測試運行器通過InstrumentationRegistry
管理它,其中存儲了由儀器框架創建的上下文和環境設置。
要訪問Instrumentation
類的實例,只需在InstrumentationRegistry
類上調用靜態方法getInstrumentation()
即可:
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation()
在本地構建和測試
對於最常見的用例,請使用Atest 。
對於需要更多定制的更複雜的情況,請遵循檢測說明。