You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android应用代码(MainActivity)中调用Instrumented Tests

问题:通过应用自身UI触发Instrumented测试用例

常规调用方式

Android平台上,Instrumented Tests通常通过以下方式调用:

  • 在Android Studio中右键点击androidTest目录下的测试类执行
  • 在CMD命令行执行命令:adb shell am instrument -w com.example.testing/androidx.test.runner.AndroidJUnitRunner

需求

希望通过应用自身UI的按钮点击事件,触发androidTest目录下指定的测试用例(点击buttonRunTest1执行testCaseForButton1,点击buttonRunTest2执行testCaseForButton2)。

现有代码

androidTest目录下的测试类代码

package com.example.myapplication

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.UiDevice

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
    @Test
    fun testCaseForButton1() {
        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
        // 测试步骤
        assertEquals("com.example.myapplication", appContext.packageName)
    }

    @Test
    fun testCaseForButton2() {
        val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
        // 测试步骤
        assertEquals("com.sec.android.app.launcher", device.launcherPackageName)
    }
}

MainActivity代码片段

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    findViewById<Button>(R.id.buttonRunTest1)
        .setOnClickListener {
            // 需要在这里实现调用testCaseForButton1的逻辑
        }

    findViewById<Button>(R.id.buttonRunTest2)
        .setOnClickListener {
            // 需要在这里实现调用testCaseForButton2的逻辑
        }
}

已尝试的无效方法

方法1:直接执行am instrument命令

fun startTest(){
   val packageName = "com.example.myapplication"
   val testRunnerClass = "androidx.test.runner.AndroidJUnitRunner"
   val command = "am instrument -w $packageName/$testRunnerClass"
   Runtime.getRuntime().exec(command)
}

方法2:反射调用Instrumentation方法

fun startTest(){
   val instrumentation = InstrumentationRegistry.getInstrumentation()
   val paramTypes = arrayOf(ComponentName::class.java, Bundle::class.java)
   val method = instrumentation.javaClass.getDeclaredMethod("startInstrumentation", *paramTypes)
}

现有配置

build.gradle(defaultConfig)

defaultConfig {
    applicationId "com.example.myapplication"
    minSdk 28
    targetSdk 32
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

依赖配置

dependencies {
    ...
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
    implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
    implementation 'androidx.test:runner:1.4.0'
    androidTestImplementation 'androidx.test:runner:1.4.0'
    androidTestImplementation 'androidx.test:rules:1.5.0'
}

可行解决方案

Instrumented测试默认运行在独立于主应用的进程中,主应用进程无法直接调用测试类代码(会报"No instrumentation registered! Must run under a registering instrumentation"错误),必须通过启动instrumentation进程并指定单个测试方法的方式实现需求。

按钮点击事件实现代码

修改MainActivity的按钮点击逻辑,在子线程中执行带参数的am instrument命令,指定要运行的测试类和方法:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    // 触发testCaseForButton1的按钮
    findViewById<Button>(R.id.buttonRunTest1).setOnClickListener {
        Thread {
            runSpecificTest("com.example.myapplication.ExampleInstrumentedTest#testCaseForButton1")
        }.start()
    }

    // 触发testCaseForButton2的按钮
    findViewById<Button>(R.id.buttonRunTest2).setOnClickListener {
        Thread {
            runSpecificTest("com.example.myapplication.ExampleInstrumentedTest#testCaseForButton2")
        }.start()
    }
}

/**
 * 执行指定的Instrumented测试用例
 * @param testSpec 格式:包名.测试类名#测试方法名
 */
private fun runSpecificTest(testSpec: String) {
    try {
        val packageName = "com.example.myapplication"
        val testRunner = "androidx.test.runner.AndroidJUnitRunner"
        // 构造带参数的am instrument命令,指定单个测试用例
        val command = "am instrument -w -e class $testSpec $packageName/$testRunner"
        val process = Runtime.getRuntime().exec(command)

        // 读取测试输出(可选,用于调试或展示结果)
        val inputReader = BufferedReader(InputStreamReader(process.inputStream))
        val errorReader = BufferedReader(InputStreamReader(process.errorStream))
        var line: String?
        while (inputReader.readLine().also { line = it } != null) {
            Log.d("TestResult", line ?: "")
        }
        while (errorReader.readLine().also { line = it } != null) {
            Log.e("TestError", line ?: "")
        }

        // 等待测试进程结束
        val exitCode = process.waitFor()
        Log.d("TestExit", "测试结束,退出码:$exitCode")
    } catch (e: Exception) {
        e.printStackTrace()
        Log.e("TestRunner", "执行测试失败:${e.message}")
    }
}

注意事项

  1. 必须在子线程执行:避免主线程阻塞导致ANR
  2. 测试路径要准确:testSpec的格式必须是「完整包名.测试类名#测试方法名」,不能写错
  3. 仅支持Debug版本:该方式需要应用处于可调试状态,Release版本可能会因为权限或代码混淆无法正常运行
  4. 权限要求:无需额外权限,am instrument命令属于系统命令,应用自身调用无需特殊权限

内容的提问来源于stack exchange,提问作者GBN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 18:47:56