背景
在Android Settings 单元测试 | Telephony Network 模块 APN 案例中粗略介绍了单元测试逻辑内容,但是在独立APK里面如何将单元测试跑起来还是有疑问,因为APP不能直接install,无法借助Android Studio直接Run,在安装的一步会报错由于未签名。
> Task :connectedDebugAndroidTest FAILED
Exception thrown during onBeforeAll invocation of plugin AndroidTestApkInstallerPlugin: ErrorName: INSTALL_FAILED_UPDATE_INCOMPATIBLE
NameSpace: DdmlibAndroidDeviceController
ErrorCode: 1
ErrorType: TEST
Message: Failed to install split APK(s): [E:\code\DemoUnit\build\intermediates\apk\debug\DemoUnit.apk]
Failed to install split APK(s): [E:\code\DemoUnit\build\intermediates\apk\debug\DemoUnit.apk]
Failed to commit install session 396951782 with command package install-commit 396951782. Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.demo.unit signatures do not match newer version; ignoring!
com.android.ddmlib.InstallException: Failed to commit install session 396951782 with command package install-commit 396951782. Error: INSTALL_FAILED_UPDATE_INCOMPATIBLE: Existing package com.demo.unit signatures do not match newer version; ignoring!
因为是签名限制的问题,就考虑尝试使用debug版本,将没有签名的APK push到system/priv-app目录下重启使之生效,结果无法正常开机,因此这个方法是不可行的。
甚至在开机过程PMS都没有起来,执行adb install 命令是返回“cmd: Can't find service: package”。
然后把debug未签名的apk删除以后,就算没有原本的apk也能直接正常开机。
如何运行Unit Test?
包含androidTest测试逻辑的APK安装到了软件怎么用命令跑单元测试?
假设应用的包名为 com.example.myapp
,测试类为 ExampleInstrumentedTest
,执行所有测试的方法可以用以下命令:
adb shell am instrument -w -r -e debug false -e class <your.package.name.YourTestClass> <your.package.name.test/androidx.test.runner.AndroidJUnitRunner># <your.package.name.YourTestClass>: 替换为你想要执行的测试类的全名。
# <your.package.name.test/androidx.test.runner.AndroidJUnitRunner>:
# 是测试 APK 的包名,后面加上 androidx.test.runner.AndroidJUnitRunner。
如:
案例1:
adb shell am instrument -w -r -e debug false -e class com.demo.unit.ExampleInstrumentedTest com.demo.unit.test/androidx.test.runner.AndroidJUnitRunner
Note:斜体是需要替换的包名
- com.demo.unit.ExampleInstrumentedTest 是代码类packages包名,在java文件头定义的
- com.demo.unit.test 是应用包名,可以通过pm查看,是在build.gradle有定义的applicationId
- 如果应用是一个插件,也不用换成宿主的包名。
案例2:
adb shell am instrument -w -r -e debug false -e class com.demo.settings.PreferenceTest com.demo.unit/androidx.test.runner.AndroidJUnitRunner
Note:
- 单元测试是包名是com.demo.unit,通过pm path可查路径。
- 代码路径包名是com.demo.settings,测试类为PreferenceTest.java。
这种执行结果虽然fail了,但是说明命令是成功的。
如果只想执行某个特定的测试方法,可以通过如下命令:
adb shell am instrument -w -r -e debug false -e class <your.package.name.YourTestClass#yourTestMethod> <your.package.name.test/androidx.test.runner.AndroidJUnitRunner># yourTestMethod: 替换为想要执行的测试方法的名称。
常见问题
问题1:
运行命令报错,表示 Android 系统无法找到指定的测试 Runner。这通常意味着测试 Runner 没有在 AndroidManifest.xml
中正确声明,或者测试 APK 没有被正确安装。
INSTRUMENTATION_STATUS: Error=Unable to find instrumentation info for: ComponentInfo{com.demo.unit/androidx.test.runner.AndroidJUnitRunner}
解决方案:在AndroidManifest.xml 添加 AndroidJUnitRunner
声明。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"coreApp="true"package="com.android.settings"android:sharedUserId="android.uid.system"><uses-sdk android:minSdkVersion="28" /><instrumentationandroid:name="androidx.test.runner.AndroidJUnitRunner"android:targetPackage="com.demo.unit" />
并通过如下命令确保应用已经安装
adb shell pm list packages | grep com.demo.unit
问题2:
android.util.AndroidException: INSTRUMENTATION_FAILED: com.demo.unit/androidx.test.runner.AndroidJUnitRunner
at com.android.commands.am.Instrument.run(Instrument.java:543)
at com.android.commands.am.Am.runInstrument(Am.java:213)
at com.android.commands.am.Am.onRun(Am.java:85)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:62)
at com.android.commands.am.Am.main(Am.java:54)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:394)
测试报告
adb 命令执行的单元测试不像在android studio 里面跑的(),不能直接生成可视化的测试报告,只能通过打印文本结果,在原本命令追加输出打印。
adb shell am instrument -w -r -e debug false -e class com.demo.settings.PreferenceTest com.demo.unit/androidx.test.runner.AndroidJUnitRunner > test_result.txt
如何编写单元测试?
能参考Google的套件包实现吗?