如果你也在学单元测试,不妨看一下这几篇文章:
咱们平时改代码时经常会有牵一发而动全身全身的情况,改完之后比较慌,不知道会引起什么问题,怕有考虑不全的情况,摁下这个问题那个问题又起来了,怎么避免这种情况呢?
考虑引入单元测试吧,它可以为我们的软件质量提供强有力的保证。
我们在单元测试中经常用的库有JUnit,Mockito,Robolectric,Espresso。
- 如果你的测试对Android框架有依赖,最好使用Robolectric;
- 如果你的测试对Android框架依赖性极小,或如果测试仅取决于你自己的对象,可以使用Mockito等;
测试金字塔包含三类测试:小型测试,中型测试,大型测试。
- 小型测试是指单元测试,用于验证应用的行为,一次验证一个类。
- 中型测试是指集成测试,用户验证模块内堆栈之间的互动或相关模块之间的互动。
- 大型测试是指端到端测试,用于验证跨越了应用的多个模块的用户操作流程。
通常建议各类测试占比为:小型测试占 70%,中型测试占20%,大型测试占10%。
除了这些测试还包括性能测试,monkey测试等。
1.基础知识
我们新建一个工程之后通常包含两个测试目录,androidTest和test目录。
- test目标主要包含运行本地的测试内容,比如单元测试,称为本地测试;
- androidTest目录包括运行在真机或虚拟设备上的测试内容,例如集成测试,端到端测试以及其他一些只依赖JVM无法完成的测试,称为仪器测试。
以下内容建议使用单元测试:ViewModels或Presenters,数据层尤其是repositories,工具类等。
单元测试中要覆盖正常的case和边界case,比如网络错误,除以0等情况。
不要用单元测试验证不属于你的代码,比如framework或库的正确行为。
activities,fragments或services等系统入口,因为没有太多逻辑,所以不适合用单元测试验证。
屏幕UI测试包含用户交互行为,如点击,输入等,建议每个界面一个测试类。
用户交互测试验证用户界面跳转流程。
添加测试依赖:
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"
}
testImplementation为本地测试添加依赖,添加的依赖只在test目录可用。
androidTestImplementation为仪器测试添加依赖,添加的依赖只在androidTest目录可用 。
通过他们添加的依赖都不会添加到最终生成的apk中。
本地测试和仪器测试的区别是他们运行方式不同。
本地测试运行在你开发设备的JVM上,不需要运行在虚拟机或物理设备上,这种方式运行速度快,但不一定真实可靠,因为它不能完全模拟真实环境。
仪器测试运行在真机或虚拟设备上,它更能反映真实的运行环境,但运行速度相对较慢
2.运行本地测试
运行本地测试的方式很多,找到一种自己习惯的就行,比如:右键单击测试文件,选择运行。
图中的6个点均能运行本地测试,其中6的位置,右键点击某个方法,可只运行某个方法。
务必选中左上角的✔️和取消图标,这样能确保测试结果不会存在遗漏。
3.运行仪器测试
仪器测试的运行方式和本地测试不同,仪器测试需要运行在虚拟机或真机设备,点击上图中右侧的运行按钮或右键点击源文件选择运行均可。
本地测试和仪器测试的区别是是否需要使用Android 系统或Android framework能力。
4.创建本地测试
4.1创建本地测试文件
右键点击你要创建测试的方法,选择Generate > Test.
弹出创建测试的弹框,修改类名称,点击OK。
选择test目录,因为我们要创建本地测试。
打开默认创建的测试文件,接下来我们要创建测试方法了。
4.2创建测试方法
class StatisticsUtilsTest {
@Test
fun getActiveAndCompletedStats_noCompleted_returnsHundredZero() {
// Create an active task (the false makes this active)
val tasks = listOf<Task>(
Task("title", "desc", isCompleted = false)
)
// Call your function
val result = getActiveAndCompletedStats(tasks)
// Check the result
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
}
}
创建测试方法,添加@Test注解,添加测试内容,使用Junit断言进行测试。
接下来右键运行即可。
官方demo中还使用了Hamcrest来写出更宜读的测试代码,比如
// REPLACE
assertEquals(result.completedTasksPercent, 0f)
assertEquals(result.activeTasksPercent, 100f)
// WITH
assertThat(result.activeTasksPercent, `is`(100f))
assertThat(result.completedTasksPercent, `is`(0f))
后面应该会单独拿出一篇学习Hamcrest。
4.3 测试用例命名规则
subjectUnderTest_actionOrInput_resultState
subject under test 是要被测试的方法或类,比如:getActiveAndCompletedStats
接下来是动作或输入,比如:noCompleted
最后是期待的结果,比如:returnsHundredZero
5.使用AndroidX Test测试ViewModel
AndroidX Test后边应该会单独用一篇文章进行学习。
先创建ViewModel测试类文件,方法类似上边创建本地测试方式,这里仍然选择本地测试目录。
AndroidX Test库包含提供用来测试的系统组件比如Apllications和Activities的类和方法。
使用AndroidX Test需要遵循如下步骤:
- 添加AndroidX Test依赖。
- 添加 Robolectric依赖。
testImplementation "androidx.test.ext:junit-ktx:$androidXTestExtKotlinRunnerVersion"
testImplementation "androidx.test:core-ktx:$androidXTestCoreVersion"
testImplementation "org.robolectric:robolectric:$robolectricVersion"
- 添加AndroidJunit4注解。
@RunWith(AndroidJUnit4::class)
class TasksViewModelTest {
// Test code
}
4、编写AndroidX 测试代码。
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
// TODO test LiveData
}
可以通过ApplicationProvider.getApplicationContext()获取application context。
AndroidX 测试API在本地测试和仪器测试中均可正常运行。在仪器测试中,ApplicationProvider.getApplicationContext()会从虚拟机或真机中获取context,在本地测试中,它会使用模拟的Android环境获取。
Robolectric提供AndroidX Test在本地测试中使用的模拟Android环境。
@RunWith(AndroidJUnit4::class)注解的作用是什么?
它用来指定要运行的runner,test runner是一个运行测试的Junit组件。
接下来我们使用InstantTaskExecutorRule测试LiveData
InstantTaskExecutorRule是一个Junit Rule,它和注解@get:Rule一起使用,当你需要测试LiveData时,使用此Rule。
确保你已添加依赖
testImplementation "androidx.arch.core:core-testing:$archTestingVersion"
在测试文件中添加
class TasksViewModelTest {
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
// Other code...
}
给LiveData添加观察者
@Test
fun addNewTask_setsNewTaskEvent() {
// Given a fresh ViewModel
val tasksViewModel = TasksViewModel(ApplicationProvider.getApplicationContext())
// Create observer - no need for it to do anything!
val observer = Observer<Event<Unit>> {}
try {
// Observe the LiveData forever
tasksViewModel.newTaskEvent.observeForever(observer)
// When adding a new task
tasksViewModel.addNewTask()
// Then the new task event is triggered
val value = tasksViewModel.newTaskEvent.value
assertThat(value?.getContentIfNotHandled(), (not(nullValue())))
} finally {
// Whatever happens, don't forget to remove the observer!
tasksViewModel.newTaskEvent.removeObserver(observer)
}
}
单元测试入门基本就到这了,接下来应该还会有几篇文章和单元测试有关。
参考: