Android 应用测试的各种环境问题记录(Instrumentation测试)
报错记录
failed to configure packages targetSdkVersion(未解决)
failed to configure com.demo.test.SettingsActivityTest.testOnCreate_withNullSavedInstanceState: Package targetSdkVersion=34 > maxSdkVersion=32
java.lang.IllegalArgumentException: failed to configure com.demo.test.SettingsActivityTest.testOnCreate_withNullSavedInstanceState: Package targetSdkVersion=34 > maxSdkVersion=32
at
Failed to release mocks 第三方mock对象释放失败
@RunWith(AndroidJUnitRunner.class),错误表明使用Mockito框架在释放对象的时候fail,原因可能是使用过了第三方的mock maker。
INSTRUMENTATION_STATUS: stack=org.mockito.exceptions.base.MockitoException:
Failed to release mocksThis should not happen unless you are using a third-party mock maker
at com.demo.UITest.setUp(UITest.java:31)
... 32 trimmed
Caused by: org.mockito.exceptions.base.MockitoException: Cannot read state from field: private com.demo.UITest com.demo.UITest.fragment, on instance: com.demo.UITest@9254eb6
... 34 more
Caused by: java.lang.IllegalStateException: Could not initialize plugin: interface org.mockito.plugins.MemberAccessor (alternate: null)
at org.mockito.internal.configuration.plugins.PluginLoader$1.invoke(PluginLoader.java:84)... 34 more
Caused by: java.lang.IllegalStateException: Failed to load interface org.mockito.plugins.MemberAccessor implementation declared in java.lang.CompoundEnumeration@5bce1d5
at org.mockito.internal.configuration.plugins.PluginInitializer.loadImpl(PluginInitializer.java:56)
可能是因为原本使用了Mockito 5,而Mockito 5 开发文档中提到 由于我们使用了 JVM 内部 API,我们发现 JDK 最新版本的问题/不兼容性有所增加。最值得注意的是,JDK 17 进行了一些与当前子类 mockmaker 不兼容的更改。
androidTestImplementation 'org.mockito:mockito-android:5.0.0' //适用于Android测试
参考:JDK17以上版本不兼容导致的mock maker问题:Could not initialize plugin: interface org.mockito.plugins.MockMaker-CSDN博客文章浏览阅读1.3k次,点赞10次,收藏11次。对于高版本java和springboot在使用Mockito时产生的不兼容问题_could not initialize plugin: interface org.mockito.plugins.mockmakerhttps://blog.csdn.net/daisy__forever/article/details/140871609
方法:对于Mockito 5+ 和 jdk 17+ 在内联模拟生成器将不起作用,可以使用子类模拟生成器,在build.gradle添加inline的依赖。
dependencies {androidTestImplementation 'org.mockito:mockito-android:4.0.0' //适用于Android测试implementation 'org.mockito:mockito-android:4.0.0' //适用于Android测试androidTestImplementation 'org.mockito:mockito-inline:4.0.0'
}
疑问:
1、不知道为什么需要implemention,不然Mockito类import会报错,是因为build APK用到吗?
2、是不是修改成mockito-android:4.0.0,就不需要加inline了?——实测是的,原本报错是因为5.0.0版本
Can't create handler inside thread Thread that has not called Looper.prepare() 交互界面线程
INSTRUMENTATION_STATUS: stack=java.lang.RuntimeException: Can't create handler inside thread Thread[Instr: androidx.test.runner.AndroidJUnitRunner,5,main] that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:228)
at android.os.Handler.<init>(Handler.java:130)
at androidx.preference.PreferenceFragmentCompat$1.<init>(PreferenceFragmentCompat.java:121)
at androidx.preference.PreferenceFragmentCompat.<init>(PreferenceFragmentCompat.java:121)
at com.demo.settings.PreferenceFragmentBase.<init>(PreferenceFragmentBase.java:49)
at com.demo.settings.Editor.<init>(Editor.java:103)
at com.demo.settings.EditorTest.setUp(EditorTest.java:32)
这通常表明测试代码试图在没有准备好 Looper 的线程上执行与 UI 相关的操作。结合测试代码,界面是Fragment,创建和操作 Fragment 必须遵循 Android 的 UI 线程规则。
参考解决方案:
通过activity建立UI线程,填充fragment数据,然后拉起fragment。
@RunWith(AndroidJUnit4.class)
public class DemoEditorTest {@Rulepublic ActivityTestRule<DemoSettingsActivity> activityRule =new ActivityTestRule<>(DemoSettingsActivity.class);@Testpublic void testMenuSave_showSaveDialog() throws Exception {//关键还是通过activity启动界面,不然无法运行在主线程(UI)activityRule.getActivity().runOnUiThread(new Runnable() {@Overridepublic void run() {DemoEditor fragment = new DemoEditor ();DemoEditor.demoData demoData = new DemoEditor.DemoData();fragment.setDemoData(demoData);activityRule.getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment).commitNow();}});}
}
gradle 环境问题
Android Studio 默认的SDK、gradle都是怎么配置的?
为什么删除的目录路径都会重新创建.gradle?之前就算TestDemoU删掉了,还是会创建的caches,在哪里设置?
# Windows
# 查找 GRADLE_USER_HOME
echo %GRADLE_USER_HOME%# 查找 GRADLE_HOME
echo %GRADLE_HOME%
Windows查找GRADLE_USER_HOME没有输出是没有配置的原因吗?GRADLE_HOME 已经配置,所以是有的。
如何查找Android Studio默认的配置路径?
在AS设置里面的 Gradle user home,JDK版本也在此设置:
File => Settings => Build, Execution, Deployment => Build Tools => Gradle
有关环境变量:
- GRADLE_USER_HOME
- GRADLE_LOCAL_JAVA_HOME
(点多了以后,reset按钮都没有了...)
参考:IDEA如何正确配置Gradle? GRADLE_USER_HOME 和 Gradle user home的区别_grade user home-CSDN博客文章浏览阅读6.1w次,点赞55次,收藏153次。IDEA如何正确配置Gradle? GRADLE_USER_HOME 和 Gradle user home的区别缘起目标GRADLE_USER_HOME和Gradle user home的区别GRADLE_USER_HOMEGradle user homeIDEA Gradle user home的坑如何验证这个坑?最终解决方案第一种解决方案,不下载Gradle,不配置关于Gradle的任何的环境变量,不配置IDEA的Gradle user home第二种方案:如果你想自己安装gradle,并且所有的工程_grade user homehttps://blog.csdn.net/iot_ai/article/details/106617626
难怪控制台命令 ./gradlew 都fail的,但其实设置了多少都没办法解决一些class问题。
测试方案和工具选型
到底选择test还是androidTest目录实现测试?
会不会有些问题就是目录不对呢?不能直接运行单元测试,必须用仪器测试?
在Android项目中,
androidTest
和test
目录有着不同的用途,主要用于不同类型的测试。
test
目录:
- 该目录用于单元测试(Unit Tests)。
- 单元测试是对应用中最小可测试单元的验证,通常是对单个类或方法的测试。
- 这些测试可以在本地环境中运行,不依赖于Android的框架。
- 使用JUnit等测试框架来编写和运行这些测试。
androidTest
目录:
- 该目录用于仪器测试(Instrumentation Tests),也称为功能测试(Functional Tests)或集成测试(Integration Tests)。
- 这些测试可以验证应用程序在Android设备或模拟器上的行为,通常涉及到多个组件的交互。
- 需要Android设备或模拟器环境来运行,通常使用Espresso、UI Automator等测试框架。
- 它们可以访问Android的API和框架。
总结:
test
目录用于快速的单元测试,不依赖Android环境。androidTest
目录用于需要Android运行时环境的仪器测试。
比如AOSP源码中,Settings 应用的测试目录结构,没有像第三方应用开发默认的目录结构区分androidTest 和 test,而是测试用例都在test目录维护,分为单元测试unit、界面测试uitesets等。
@RunWith的选择建议
- 使用
MockitoJUnitRunner
:当你只需要测试简单的 Java 类逻辑,不依赖于 Android 框架时。 - 使用
AndroidJUnit4
:当你的测试需要运行在真实的 Android 环境中,且涉及到 UI 组件或 Android API。 - 使用
RobolectricTestRunner
:当你想要在 JVM 环境中运行 Android 测试,且希望在不依赖真实设备的情况下进行单元测试或集成测试。
1. MockitoJUnitRunner
- 主要用途:用于单元测试,特别是需要 Mockito 模拟的场景。
- 特点:
- 自动初始化 Mockito 的
mock
对象。- 适合非 Android 环境的简单 Java 类测试。
- 不支持 Android 组件和框架依赖的测试。
2. AndroidJUnit4
- 主要用途:用于 Android Instrumentation 测试,通过 Android 测试框架运行测试。
- 特点:
- 支持完整的 Android 环境,能够访问 Android API 和组件。
- 适合 UI 测试和与 Android 组件交互的测试。
- 不会自动处理 Mockito 的 mock 对象,需要手动配置。
3. RobolectricTestRunner
- 主要用途:用于在 JVM 上运行 Android 测试,模拟 Android 环境。
- 特点:
- 不需要物理设备或模拟器即可运行 Android 测试。
- 模拟 Android API,使得测试更快、更容易进行。
- 支持使用 Mockito 进行 mock 对象的创建和使用。
- 适合进行单元测试和集成测试,尤其是在需要 Android 上下文的情况下。