目标应用示例

这种类型的检测工具测试与针对常规 Android 应用的测试没有太大区别。值得注意的是,包含检测工具的测试应用需要使用与其目标应用相同的证书进行签名。

请注意,本指南假定您已掌握平台源代码树工作流的一些知识。如果不是,请参阅要求。此处介绍的示例是编写一个新的检测工具测试,其目标软件包设置为其自身的测试应用软件包。如果您不熟悉这个概念,请通读平台测试简介

本指南使用以下测试作为示例

  • frameworks/base/packages/Shell/tests

建议先浏览代码,大致了解一下,然后再继续。

确定源代码位置

由于检测工具测试将以应用为目标,因此惯例是将测试源代码放在平台源代码树中组件源代码目录根目录下的 tests 目录中。

如需详细了解源代码位置,请参阅自检测工具端到端测试示例

清单文件

与常规应用一样,每个检测工具测试模块都需要一个清单文件。如果您将文件命名为 AndroidManifest.xml 并将其与测试模块的 Android.mk 放在一起,则 BUILD_PACKAGE 核心 makefile 会自动包含该文件。

在继续之前,强烈建议先阅读应用清单概览

其中概述了清单文件的基本组件及其功能。

可以访问以下网址获取示例 Gerrit 更改的最新版本清单文件:https://android.googlesource.com/platform/frameworks/base/+/main/packages/Shell/tests/AndroidManifest.xml

为方便起见,此处包含了一个快照

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

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

        <activity
            android:name="com.android.shell.ActionSendMultipleConsumerActivity"
            android:label="ActionSendMultipleConsumer"
            android:theme="@android:style/Theme.NoDisplay"
            android:noHistory="true"
            android:excludeFromRecents="true">
            <intent-filter>
                <action android:name="android.intent.action.SEND_MULTIPLE" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
        </activity>
    </application>

    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
        android:targetPackage="com.android.shell"
        android:label="Tests for Shell" />

</manifest>

关于清单文件的一些精选备注

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.shell.tests">

package 属性是应用软件包名称:这是 Android 应用框架用于标识应用(或在此上下文中:您的测试应用)的唯一标识符。系统中每个用户只能安装一个具有该软件包名称的应用。

由于这是一个测试应用软件包,独立于被测应用软件包,因此必须使用不同的软件包名称:一个常见的惯例是添加后缀 .test

此外,此 package 属性与 ComponentName#getPackageName() 返回的值相同,并且与您通过 adb shell 与各种 pm 子命令交互时使用的值也相同。

另请注意,虽然软件包名称的样式通常与 Java 软件包名称相同,但实际上它与 Java 软件包名称几乎无关。换句话说,您的应用(或测试)软件包可能包含具有任何软件包名称的类,但另一方面,您可以选择简单化,并使应用或测试中的顶级 Java 软件包名称与应用软件包名称相同。

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

所有检测工具测试都需要这样做,因为相关的类打包在单独的框架 jar 库文件中,因此当应用框架调用测试软件包时,需要额外的类路径条目。

android:targetPackage="com.android.shell"

这会将检测工具的目标软件包设置为 com.android.shell。当通过 am instrument 命令调用检测工具时,框架会重启 com.android.shell 进程,并将检测工具代码注入到该进程中以执行测试。这也意味着测试代码将有权访问被测应用中运行的所有类实例,并且可能能够根据公开的测试挂钩来操纵状态。

简单配置文件

每个新的测试模块都必须有一个配置文件,以指导构建系统使用模块元数据、编译时依赖项和打包说明。在大多数情况下,基于 Soong 的 Blueprint 文件选项就足够了。有关详情,请参阅简单测试配置

复杂配置文件

对于更复杂的测试,您还需要为 Android 的测试框架 Trade Federation 编写测试配置文件。

测试配置可以指定特殊的设备设置选项和默认参数,以提供给测试类。

可以访问以下网址获取示例 Gerrit 更改的最新版本配置文件:frameworks/base/packages/Shell/tests/AndroidTest.xml

为方便起见,此处包含了一个快照

<configuration description="Runs Tests for Shell.">
    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
        <option name="test-file-name" value="ShellTests.apk" />
    </target_preparer>

    <option name="test-suite-tag" value="apct" />
    <option name="test-tag" value="ShellTests" />
    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
        <option name="package" value="com.android.shell.tests" />
        <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="ShellTests.apk"/>
</target_preparer>

这会告知 Trade Federation 使用指定的 target_preparer 将 ShellTests.apk 安装到目标设备上。Trade Federation 中有许多目标准备器可供开发者使用,这些准备器可用于确保在执行测试之前正确设置设备。

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

这指定了要用于执行测试的 Trade Federation 测试类,并传入要在设备上执行的软件包和测试运行程序框架(在本例中为 JUnit)。

如需详细了解测试模块配置,请点击此处。

JUnit4 功能

使用 android-support-test 库作为测试运行程序可以采用新的 JUnit4 样式测试类,并且示例 Gerrit 更改包含对其某些非常基本功能的使用。

可以访问以下网址获取示例 Gerrit 更改的最新源代码:frameworks/base/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java

虽然测试模式通常特定于组件团队,但也有一些通用的使用模式。

@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FeatureFactoryImplTest {

JUnit4 的一个显著区别是,测试不再需要从公共基类继承;相反,您可以在普通的 Java 类中编写测试,并使用注解来指示某些测试设置和约束。在本例中,我们指示应将此类作为 Android JUnit4 测试运行。

@SmallTest 注解为整个测试类指定了测试规模:添加到此测试类中的所有测试方法都继承此测试规模注解。pre test class setup、post test tear down 和 post test class tear down:类似于 JUnit4 中的 setUptearDown 方法。Test 注解用于注解实际测试。

    @Before
    public void setup() {
    ...
    @Test
    public void testGetProvider_shouldCacheProvider() {
    ...

@Before 注解在 JUnit4 的方法中使用,以执行 pre test setup。虽然本例中未使用,但也有 @After 用于 post test teardown。同样,@BeforeClass@AfterClass 注解可以在 JUnit4 的方法中使用,以在执行测试类中的所有测试之前执行 setup,并在之后执行 teardown。请注意,类范围的 setup 和 teardown 方法必须是静态的。

至于测试方法,与早期版本的 JUnit 不同,它们不再需要以 test 开头的方法名称,而是每个方法都必须使用 @Test 进行注解。与往常一样,测试方法必须是公共的,不声明返回值,不接受参数,并且可能会抛出异常。

        Context context = InstrumentationRegistry.getTargetContext();

由于 JUnit4 测试不再需要公共基类,因此不再需要通过基类方法通过 getContext()getTargetContext() 获取 Context 实例;相反,新的测试运行程序通过 InstrumentationRegistry 管理它们,其中存储了检测工具框架创建的上下文和环境设置。通过此类,您还可以调用

  • getInstrumentation()Instrumentation 类的实例
  • getArguments():通过 -e <key> <value> 传递给 am instrument 的命令行参数

在本地构建和测试

对于最常见的用例,请使用 Atest

对于需要更重度自定义的更复杂情况,请按照检测工具说明进行操作。