Activity单元测试Instrumentation入门

359 阅读2分钟

前言

我们用Android Studio创建一个新的项目的时候,会发现在main目录的同级有两个目录test和androidTest,这两个就是单元测试代码,其中androidTest下就是Instrumentation测试代码。

今天我们来看看Instrumentation的简单使用。

已有项目

上面是新建项目的情况,如果是已有项目该如何添加单元测试?

首先要依赖相关的几个库,如下:

testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

然后要在build.gradle中添加testInstrumentationRunner

android {
    compileSdk 31

    defaultConfig {
        applicationId "com.xxx.junittest"
        minSdk 21
        targetSdk 26
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    
    ...

然后在src目录下创建新的目录,在创建窗口中直接选择“androidTest/java”这个选项即可

image.png 创建目录后在该目录下创建package(与应用包名相同),然后就可以创建测试类编写代码了。

测试类

一个简单的例子如下:

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun useAppContext() {
        // Context of the app under test.
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        assertEquals("com.xxxx.junittest", appContext.packageName)
    }
}

这里通过InstrumentationRegistry.getInstrumentation().targetContext来获取应用的Context,然后校验报名。

运行测试

可以测试整个类,也可以测试单个函数,如下

image.png

图中是测试useAppContext这个函数,测试结果如下

image.png

因为我测试的包名不对,所以本次测试的结果是失败的。

测试Activity

下面我们来看一个更复杂的例子,我们需要打开一个页面,然后获取页面一个文本并检查是否正确,代码如下:

@Test
fun startMain(){
    val appContext = InstrumentationRegistry.getInstrumentation().targetContext
    val intent = Intent(appContext, MainActivity::class.java)
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    val activity = InstrumentationRegistry.getInstrumentation().startActivitySync(intent)
    val textView = activity.findViewById<TextView>(R.id.textview)
    assertEquals("hello", textView.text)
}

这里我们用startActivitySync来启动页面,然后获取id是R.id.textview的TextView,并检查其文本。

在测试过程中出现一个问题,运行这段代码一直不成功,报错如下:

java.lang.RuntimeException: Could not launch intent Intent { flg=0x14000000 cmp=com.xxx.junittest/.MainActivity } within 45000 milliseconds. Perhaps the main thread has not gone idle within a reasonable amount of time? There could be an animation or something constantly repainting the screen. Or the activity is doing network calls on creation? See the threaddump logs. For your reference the last time the event queue was idle before your activity launch request was 1651831105045 and now the last time the queue went idle was: 1651831105045. If these numbers are the same your activity might be hogging the event queue.

at androidx.test.runner.MonitoringInstrumentation.startActivitySync(MonitoringInstrumentation.java:490)

...

网上说是Android Studio的HotSwap功能导致的,但是关闭也不行,最后发现是手机问题,在小米手机上就报错,换成一个华为手机就可以了。

保持Activity打开

运行上面的代码可以发现activity只打开一瞬间就关闭了,如果我们想让activity保持打开的状态以便做一些操作呢?其实很简单,加一个sleep就可以了,如下:

@Test
fun startMain(){
    val appContext = InstrumentationRegistry.getInstrumentation().targetContext
    val intent = Intent(appContext, MainActivity::class.java)
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    InstrumentationRegistry.getInstrumentation().startActivitySync(intent)
    sleep(5000)
}

这样页面打开后就会停留5秒。

总结

后续我们可以利用Instrumentation创建更多的测试,以便完善我们的应用。