本地测试直接在您自己的工作站上运行,而不是在 Android 设备或模拟器上。因此,它使用您的本地 Java 虚拟机 (JVM),而不是 Android 设备来运行测试。本地测试使您能够更快地评估应用的逻辑。但是,无法与 Android 框架交互会限制您可以运行的测试类型。
单元测试验证一小段代码(被测单元)的行为。它通过执行该代码并检查结果来做到这一点。
单元测试通常很简单,但当被测单元在设计时未考虑可测试性时,其设置可能会出现问题。
- 您要验证的代码需要从测试中访问。例如,您不能直接测试私有方法。相反,您可以使用其公共 API 测试类。
- 为了在隔离状态下运行单元测试,被测单元的依赖项必须替换为您控制的组件,例如模拟对象或其他测试替身。如果您的代码依赖于 Android 框架,这尤其成问题。
要了解 Android 中常见的单元测试策略,请阅读要测试的内容。
本地测试位置
默认情况下,本地单元测试的源文件放置在module-name/src/test/
中。使用 Android Studio 创建新项目时,此目录已存在。
添加测试依赖项
您还需要配置项目的测试依赖项以使用JUnit测试框架提供的标准 API。
为此,请打开应用模块的build.gradle
文件,并将以下库指定为依赖项。使用testImplementation
函数指示它们应用于本地测试源集,而不是应用
dependencies {
// Required -- JUnit 4 framework
testImplementation "junit:junit:$jUnitVersion"
// Optional -- Robolectric environment
testImplementation "androidx.test:core:$androidXTestVersion"
// Optional -- Mockito framework
testImplementation "org.mockito:mockito-core:$mockitoVersion"
// Optional -- mockito-kotlin
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockitoKotlinVersion"
// Optional -- Mockk framework
testImplementation "io.mockk:mockk:$mockkVersion"
}
创建本地单元测试类
您可以将本地单元测试类编写为JUnit 4测试类。
为此,请创建一个包含一个或多个测试方法的类,通常位于module-name/src/test/
中。测试方法以@Test
注释开头,并包含用于执行和验证要测试的组件的单个方面的代码。
以下示例演示了如何实现本地单元测试类。测试方法emailValidator_correctEmailSimple_returnsTrue()
尝试验证isValidEmail()
,它是应用中的一个方法。如果isValidEmail()
也返回 true,则测试函数将返回 true。
Kotlin
import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EmailValidatorTest { @Test fun emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("[email protected]")) } }
Java
import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; class EmailValidatorTest { @Test public void emailValidator_CorrectEmailSimple_ReturnsTrue() { assertTrue(EmailValidator.isValidEmail("[email protected]")); } }
您应该创建易于阅读的测试,以评估应用程序中的组件是否返回预期结果。我们建议您使用断言库,例如 junit.Assert、Hamcrest 或 Truth。以上代码片段展示了如何使用 junit.Assert
。
可模拟的 Android 库
当您执行本地单元测试时,Android Gradle 插件 包含一个库,其中包含 Android 框架的所有 API,这些 API 与项目中使用的版本一致。该库包含这些 API 的所有公共方法和类,但方法内部的代码已被移除。如果访问了任何方法,测试将抛出异常。
这允许在引用 Android 框架中的类(例如 Context
)时构建本地测试。更重要的是,它允许您将模拟框架与 Android 类一起使用。
模拟 Android 依赖项
一个典型的问题是发现某个类正在使用字符串资源。您可以通过调用 Context
类中的 getString()
方法来获取字符串资源。但是,本地测试无法使用 Context
或其任何方法,因为它们属于 Android 框架。理想情况下,对 getString()
的调用将从类中移出,但这并非总是可行的。解决方案是创建一个 Context
的模拟对象或存根,当调用其 getString()
方法时始终返回相同的值。
使用可模拟的 Android 库和模拟框架(例如 Mockito 或 MockK),您可以在单元测试中编写 Android 类的模拟对象的行为。
要使用 Mockito 将模拟对象添加到本地单元测试中,请遵循以下编程模型
- 在您的
build.gradle
文件中包含 Mockito 库依赖项,如 设置测试环境 中所述。 - 在单元测试类定义的开头,添加
@RunWith(MockitoJUnitRunner.class)
注解。此注解告诉 Mockito 测试运行程序验证您对框架的使用是否正确,并简化模拟对象的初始化。 - 要为 Android 依赖项创建模拟对象,请在字段声明之前添加
@Mock
注解。 - 要存根依赖项的行为,您可以使用
when()
和thenReturn()
方法指定条件和满足条件时的返回值。
以下示例显示了如何创建一个使用 Kotlin 中创建的模拟 Context
对象的单元测试,该测试使用 Mockito-Kotlin 创建。
import android.content.Context
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.junit.MockitoJUnitRunner
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
private const val FAKE_STRING = "HELLO WORLD"
@RunWith(MockitoJUnitRunner::class)
class MockedContextTest {
@Mock
private lateinit var mockContext: Context
@Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
val mockContext = mock<Context> {
on { getString(R.string.name_label) } doReturn FAKE_STRING
}
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getName()
// ...then the result should be the expected one.
assertEquals(result, FAKE_STRING)
}
}
要了解有关使用 Mockito 框架的更多信息,请参阅 Mockito API 参考 和 示例代码 中的 SharedPreferencesHelperTest
类。还可以尝试 Android 测试 Codelab。
错误:“方法…未模拟”
如果您尝试使用任何方法访问可模拟的 Android 库,该库将抛出异常,并显示消息 Error: "Method ... not mocked
。
如果抛出的异常对您的测试有影响,您可以更改行为,以便方法返回 null 或零,具体取决于返回类型。为此,请在项目的顶级 build.gradle
文件(Groovy 中)中添加以下配置
android {
...
testOptions {
unitTests.returnDefaultValues = true
}