构建插桩单元测试

插桩单元测试是在实体设备和模拟器上运行的测试,此类测试可以利用 Android 框架 API 和辅助性 API,如 AndroidX Test。插桩测试提供的保真度比本地单元测试要高,但运行速度要慢得多。因此,我们建议只有在必须针对真实设备的行为进行测试时才使用插桩单元测试。AndroidX Test 提供了几个库,可让您在必要时更轻松地编写插桩单元测试。例如,Android Builder 类可让您更轻松地创建本来难以构建的 Android 数据对象。

注意:如果您的测试依赖于您自己的依赖项,请提供您自己的虚假对象或使用模拟框架(如 Mockito)来模拟依赖项。

设置测试环境

在 Android Studio 项目中,您必须将插桩测试的源文件存储在 module-name/src/androidTest/java/ 中。此目录在您创建新项目时已存在,并且包含一个插桩测试示例。

在开始之前,您应先添加 AndroidX Test API,以便为您的应用快速构建和运行插桩测试代码。AndroidX Test 包含 JUnit 4 测试运行程序 (AndroidJUnitRunner) 和用于功能界面测试的 API(EspressoUI Automator)。

您还需要为项目配置 Android 测试依赖项,以使用 AndroidX Test 提供的测试运行程序和规则 API。为了简化测试开发,您还应添加 Hamcrest 库,该库可让您使用 Hamcrest 匹配器 API 创建更灵活的断言。

在应用的顶级 build.gradle 文件中,您需要将以下库指定为依赖项:

    dependencies {
        androidTestImplementation 'androidx.test:runner:1.1.0'
        androidTestImplementation 'androidx.test:rules:1.1.0'
        // Optional -- Hamcrest library
        androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
        // Optional -- UI testing with Espresso
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
        // Optional -- UI testing with UI Automator
        androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
    }
    

如需使用 JUnit 4 测试类,请务必在您的项目中将 AndroidJUnitRunner 指定为默认插桩测试运行程序,方法是在应用的模块级 build.gradle 文件中添加以下设置:

    android {
        defaultConfig {
            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }
    }
    

创建插桩单元测试类

插桩单元测试类应该是一个 JUnit 4 测试类,它类似于有关如何创建本地单元测试类的部分中介绍的类。

如需创建 JUnit 4 插桩测试类,请将 AndroidJUnit4 指定为默认测试运行程序。

注意:如果您的测试套件依赖于 JUnit3 和 JUnit4 库的混合搭配,请在测试类定义的开头添加 @RunWith(AndroidJUnit4::class) 注释。

以下示例展示了如何编写插桩单元测试来验证是否为 LogHistory 类正确实现了 Parcelable 接口:

Kotlin

    import android.os.Parcel
    import android.text.TextUtils.writeToParcel
    import androidx.test.filters.SmallTest
    import androidx.test.runner.AndroidJUnit4
    import com.google.common.truth.Truth.assertThat
    import org.junit.Before
    import org.junit.Test
    import org.junit.runner.RunWith

    const val TEST_STRING = "This is a string"
    const val TEST_LONG = 12345678L

    // @RunWith is required only if you use a mix of JUnit3 and JUnit4.
    @RunWith(AndroidJUnit4::class)
    @SmallTest
    class LogHistoryAndroidUnitTest {
        private lateinit var logHistory: LogHistory

        @Before
        fun createLogHistory() {
            logHistory = LogHistory()
        }

        @Test
        fun logHistory_ParcelableWriteRead() {
            val parcel = Parcel.obtain()
            logHistory.apply {
                // Set up the Parcelable object to send and receive.
                addEntry(TEST_STRING, TEST_LONG)

                // Write the data.
                writeToParcel(parcel, describeContents())
            }

            // After you're done with writing, you need to reset the parcel for reading.
            parcel.setDataPosition(0)

            // Read the data.
            val createdFromParcel: LogHistory = LogHistory.CREATOR.createFromParcel(parcel)
            createdFromParcel.getData().also { createdFromParcelData: List<Pair<String, Long>> ->

                // Verify that the received data is correct.
                assertThat(createdFromParcelData.size).isEqualTo(1)
                assertThat(createdFromParcelData[0].first).isEqualTo(TEST_STRING)
                assertThat(createdFromParcelData[0].second).isEqualTo(TEST_LONG)
            }
        }
    }

    

Java

    import android.os.Parcel;
    import android.util.Pair;
    import androidx.test.filters.SmallTest;
    import androidx.test.runner.AndroidJUnit4;
    import com.google.common.truth.Truth.assertThat;
    import java.util.List;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;

    // @RunWith is required only if you use a mix of JUnit3 and JUnit4.
    @RunWith(AndroidJUnit4.class)
    @SmallTest
    public class LogHistoryAndroidUnitTest {

        public static final String TEST_STRING = "This is a string";
        public static final long TEST_LONG = 12345678L;
        private LogHistory mLogHistory;

        @Before
        public void createLogHistory() {
            mLogHistory = new LogHistory();
        }

        @Test
        public void logHistory_ParcelableWriteRead() {
            // Set up the Parcelable object to send and receive.
            mLogHistory.addEntry(TEST_STRING, TEST_LONG);

            // Write the data.
            Parcel parcel = Parcel.obtain();
            mLogHistory.writeToParcel(parcel, mLogHistory.describeContents());

            // After you're done with writing, you need to reset the parcel for reading.
            parcel.setDataPosition(0);

            // Read the data.
            LogHistory createdFromParcel = LogHistory.CREATOR.createFromParcel(parcel);
            List<Pair<String, Long>> createdFromParcelData
                    = createdFromParcel.getData();

            // Verify that the received data is correct.
            assertThat(createdFromParcelData.size()).isEqualTo(1);
            assertThat(createdFromParcelData.get(0).first).isEqualTo(TEST_STRING);
            assertThat(createdFromParcelData.get(0).second).isEqaulTo(TEST_LONG);
        }
    }
    

创建测试套件

为了使插桩单元测试的执行有条不紊,您可以将一系列测试类归入一个测试套件类,并将这些测试一起运行。测试套件可以嵌套;您的测试套件可以将其他测试套件归在一起,并将其所有组件测试类一起运行。

测试套件包含在测试软件包中,类似于主应用软件包。按照惯例,测试套件软件包名称通常以 .suite 后缀结尾(例如,com.example.android.testing.mysample.suite)。

如需为您的单元测试创建测试套件,请导入 JUnit RunWithSuite 类。在您的测试套件中,添加 @RunWith(Suite.class)@Suite.SuitClasses() 注释。在 @Suite.SuiteClasses() 注释中,将各个测试类或测试套件作为参数列出。

以下示例展示了如何实现名为UnitTestSuite的测试套件,该测试套件将CalculatorInstrumentationTestCalculatorAddParameterizedTest测试类组合在一起并运行。

Kotlin

    import com.example.android.testing.mysample.CalculatorAddParameterizedTest
    import com.example.android.testing.mysample.CalculatorInstrumentationTest
    import org.junit.runner.RunWith
    import org.junit.runners.Suite

    // Runs all unit tests.
    @RunWith(Suite::class)
    @Suite.SuiteClasses(CalculatorInstrumentationTest::class,
            CalculatorAddParameterizedTest::class)
    class UnitTestSuite
    

Java

    import com.example.android.testing.mysample.CalculatorAddParameterizedTest;
    import com.example.android.testing.mysample.CalculatorInstrumentationTest;
    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;

    // Runs all unit tests.
    @RunWith(Suite.class)
    @Suite.SuiteClasses({CalculatorInstrumentationTest.class,
            CalculatorAddParameterizedTest.class})
    public class UnitTestSuite {}
    

运行插桩单元测试

要运行插桩测试,请按以下步骤操作:

  1. 点击工具栏中的 Sync Project 图标 ,确保您的项目与 Gradle 同步。
  2. 通过以下某种方式来运行测试:
    • 如需运行单个测试,请打开 Project 窗口,右键点击一个测试,然后点击 Run 图标
    • 如需测试一个类中的所有方法,请右键点击测试文件中的一个类或方法,然后点击 Run 图标
    • 如需运行一个目录中的所有测试,请右键点击该目录,然后选择 Run tests 图标

Android Plugin for Gradle 会编译位于默认目录 (src/androidTest/java/) 中的插桩测试代码,构建一个测试 APK 和一个正式版 APK,将这两个 APK 安装在连接的设备或模拟器上,并运行测试。Android Studio 随后会在“Run”窗口中显示执行插桩测试的结果。

利用 Firebase 测试实验室运行测试

使用 Firebase 测试实验室,您可以同时在许多主流 Android 设备和设备配置(语言区域、屏幕方向、屏幕尺寸和平台版本)上测试您的应用。这些测试在远程 Google 数据中心的物理设备和虚拟设备上运行。您可以直接通过 Android Studio 或从命令行将应用部署到测试实验室。测试结果会提供测试日志,并包含所有应用故障的详细信息。

在开始使用 Firebase 测试实验室之前,除非您已经拥有 Google 帐号和 Firebase 项目,否则您需要执行以下操作:

  1. 创建一个 Google 帐号(如果您还没有帐号)。
  2. Firebase 控制台中,点击新建项目

    利用测试实验室在 Spark 方案每日免费配额内测试您的应用时不收取任何费用。

配置测试矩阵并运行测试

Android Studio 提供了集成工具,可让您配置希望如何将测试部署到 Firebase 测试实验室。利用 Blaze 结算方案创建 Firebase 项目后,您可以创建测试配置并运行测试:

  1. 从主菜单中依次点击 Run > Edit Configurations
  2. 点击 Add New Configuration 图标 ,然后选择 Android Tests
  3. 在 Android 测试配置对话框中:
    1. 输入或选择测试的详细信息,如测试名称、模块类型、测试类型和测试类。
    2. 从“Deployment Target Options”下的“Target”下拉菜单中,选择 Firebase Test Lab Device Matrix
    3. 如果您未登录,请点击 Connect to Google Cloud Platform,并允许 Android Studio 访问您的帐号。
    4. 点击“Cloud Project”旁边的 按钮,并从列表中选择您的 Firebase 项目。
  4. 创建并配置测试矩阵:
    1. 点击“Matrix Configuration”下拉列表旁边的 Open Dialog 图标
    2. 点击 Add New Configuration (+)
    3. Name 字段中,输入新配置的名称。
    4. 选择要用于测试应用的设备、Android 版本、语言区域和屏幕方向。在生成测试结果时,Firebase 测试实验室会根据您所选内容的每种组合来测试您的应用。
    5. 点击 OK 以保存您的配置。
  5. 在“Run/Debug Configurations”对话框中点击 OK 以退出。
  6. 点击 Run 图标 以运行您的测试。

图 1. 为 Firebase 测试实验室创建测试配置。

分析测试结果

Firebase 测试实验室运行完您的测试后,将打开“Run”窗口以显示结果,如图 2 所示。您可能需要点击 Show Passed 图标 以查看执行的所有测试。

图 2. 使用 Firebase 测试实验室查看插桩测试的结果。

您也可以点击显示在“Run”窗口中测试执行日志开头的链接,在网页上分析您的测试。

深入阅读

如需详细了解如何解读网页上的结果,请参阅分析 Firebase Android 测试实验室结果

其他资源

如需详细了解如何在 Android 测试中使用 Espresso,请参阅以下资源。

示例

Codelab