我正在尝试使用androidx测试库的新FragmentScenario API进行本地测试和工具测试(androidTest)。该api在本地环境中工作正常,但在仪器测试中会出现错误: java.lang.AssertionError:活动永远不会变成请求状态“ [RESUMED,DESTROYED]”(上一个生命周期转换=“ PRE_ON_CREATE”)”
帮助我进行仪器测试(androidTest)
请检查完整的错误详细信息:
java.lang.AssertionError: Activity never becomes requested state "[RESUMED, DESTROYED]" (last lifecycle transition = "PRE_ON_CREATE")
at androidx.test.core.app.ActivityScenario.waitForActivityToBecomeAnyOf(ActivityScenario.java:228)
at androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:198)
at androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:169)
at androidx.fragment.app.testing.FragmentScenario.launchInContainer(FragmentScenario.java:160)
at com.techzis.avatr.LoginFragmentTest1.dummyTest(LoginFragmentTest1.kt:26)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:104)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:56)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:388)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2152)
Instrument Testing(androidTest)代码为:
@RunWith(AndroidJUnit4::class)
class LoginFragmentTest1 {
@Test
fun dummyTest() {
val scenario = launchFragmentInContainer<LoginFragment>()
onView(ViewMatchers.withId(R.id.user_name)).perform(ViewActions.typeText("Hello World!"))
onView(ViewMatchers.withId(R.id.user_name)).check(matches(withText("Hello World!")))
}
}
本地单元测试代码为:
@RunWith(AndroidJUnit4::class)
@Config(application = MyApplication::class, shadows = [ShadowAndroidXMultiDex::class])
class LoginFragmentTest2 {
@Test
fun dummyTest() {
val scenario = launchFragmentInContainer<LoginFragment>()
onView(ViewMatchers.withId(R.id.user_name)).perform(ViewActions.typeText("Hello World!"))
onView(ViewMatchers.withId(R.id.user_name)).check(matches(withText("Hello World!")))
}
}
应用程序级别的build.gradle文件为:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: "androidx.navigation.safeargs"
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.example"
minSdkVersion 18
targetSdkVersion 28
versionCode 1
versionName "1.0"
vectorDrawables.useSupportLibrary = true
multiDexEnabled false
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments clearPackageData: 'true'
}
buildTypes {
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dataBinding {
enabled = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
androidExtensions {
experimental = true
}
lintOptions {
checkReleaseBuilds false
// Or, if you prefer, you can continue to check for errors in release builds,
// but continue the build even when errors are found:
abortOnError false
}
kapt {
javacOptions {
option("-Xmaxerrs", 1000)
}
}
testOptions {
unitTests.includeAndroidResources = true
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
configurations.all {
resolutionStrategy {
force 'com.google.code.findbugs:jsr305:3.0.2'
force 'org.jetbrains.kotlin:kotlin-reflect:1.2.71'
}
}
sourceSets {
test { java.srcDirs += "$projectDir/src/testShared" }
androidTest {
java.srcDirs += "$projectDir/src/testShared"
resources.srcDirs += "$projectDir/src/test/resources"
}
}
}
dependencies {
def lifecycle_version = "2.0.0"
def fragment_version = "1.1.0-alpha01"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.multidex:multidex:2.0.0'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation "androidx.fragment:fragment:$fragment_version"
implementation 'com.google.android.material:material:1.0.0'
implementation 'androidx.recyclerview:recyclerview:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
implementation "com.squareup.moshi:moshi-kotlin:1.8.0"
implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.1'
implementation 'com.github.bumptech.glide:glide:4.8.0'
kapt 'com.github.bumptech.glide:compiler:4.8.0'
kapt "com.android.databinding:compiler:$gradle_version"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
implementation "android.arch.navigation:navigation-fragment-ktx:$nav_version"
implementation 'com.github.florent37:diagonallayout:1.1.1'
testImplementation 'androidx.test:core:1.0.0'
testImplementation 'org.robolectric:robolectric:4.1-alpha-1'
androidTestImplementation 'androidx.test:runner:1.1.0'
testImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test.ext:junit:1.0.0'
testImplementation 'androidx.test.ext:junit:1.0.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
testImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
testImplementation 'androidx.test.espresso:espresso-core:3.1.0'
androidTestImplementation 'androidx.test.ext:truth:1.0.0'
testImplementation 'androidx.test.ext:truth:1.0.0'
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'
testImplementation 'org.hamcrest:hamcrest-library:1.3'
androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13"
testImplementation "io.mockk:mockk:1.8.13.kotlin13"
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version" // Test helpers for navigation
androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
testImplementation "androidx.fragment:fragment-testing:$fragment_version"
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version" // Test helpers for LiveData
testImplementation "androidx.arch.core:core-testing:$lifecycle_version" // Test helpers for LiveData
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.10.0'
androidTestUtil 'androidx.test:orchestrator:1.1.0'
}
答案 0 :(得分:1)
您需要向测试中的APK添加“片段测试”依赖性,而不是测试APK。
因此,请将您的build.gradle更新为
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
来自
androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
(这是由于FragmentScenario的实现细节所致。“ fragment-testing”声明了Activity并由FragmentScenario使用。在测试APK中声明的Activity与受测试的APK在不同的进程中运行。为了在同一进程中执行Fragment的代码,则需要将“片段测试”库放入您的APK中,而不是在测试APK中。)
这里也是开发者网站中的tutorial page。
答案 1 :(得分:0)
I believe there is a limitation within the launch(Intent startActivityIntent)
method of ActivityScenario. It only wants the Activity to be RESUMED or DESTROYED and if it isn't within 4.5 seconds then it throws that error.
Within public static <A extends Activity> ActivityScenario<A> launch(Intent startActivityIntent)
of Activity Scenario, check the logic scenario.waitForActivityToBecomeAnyOf(State.RESUMED, State.DESTROYED);
If you can create your own custom Activity Scenario and adjust this line of code to be something like scenario.waitForActivityToBecomeAnyOf(State.ON_PRE_CREATE, State.DESTROYED);
then it will theoretically work for you.
This is happening because the Lifecycle.State
of your Activity when launching the Fragment is not either of the two specific lifecycle states specified by ActivityScenario. Again, I believe this may be a limitation of the API and I am filing an issue under Android-Test Repo. https://github.com/android/android-test/issues/new
答案 2 :(得分:0)
在我的情况下,由于
,无法打开片段所在的活动EmptyFragmentScenario。Caused by: java.lang.ClassNotFoundException: Didn't find class androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity
查看完整的堆栈跟踪:
2018-12-12 02:12:46.529 32659-32659/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: app.debug.test, PID: 32659
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{app.debug.test/androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity}: java.lang.ClassNotFoundException: Didn't find class "androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity" on path: DexPathList[[zip file "/system/framework/android.test.mock.jar", zip file "/system/framework/android.test.runner.jar", zip file "/data/app/app.debug.test-HdSyMEsvYzlt1aceQIeIuw==/base.apk"],nativeLibraryDirectories=[/data/app/app.test-HdSyMEsvYzlt1aceQIeIuw==/lib/arm64, /system/lib64, /vendor/lib64]]
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2843)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity" on path: DexPathList[[zip file "/system/framework/android.test.mock.jar", zip file "/system/framework/android.test.runner.jar", zip file "/data/app/app.debug.test-HdSyMEsvYzlt1aceQIeIuw==/base.apk"],nativeLibraryDirectories=[/data/app/app.debug.test-HdSyMEsvYzlt1aceQIeIuw==/lib/arm64, /system/lib64, /vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
at android.app.AppComponentFactory.instantiateActivity(AppComponentFactory.java:69)
at android.app.Instrumentation.newActivity(Instrumentation.java:1215)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2831)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3048)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1808)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6669)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Suppressed: java.lang.NoClassDefFoundError: Failed resolution of: Landroidx/fragment/app/FragmentActivity;
at java.lang.VMClassLoader.findLoadedClass(Native Method)
at java.lang.ClassLoader.findLoadedClass(ClassLoader.java:738)
at java.lang.ClassLoader.loadClass(ClassLoader.java:363)
... 15 more
Caused by: java.lang.ClassNotFoundException: Didn't find class "androidx.fragment.app.FragmentActivity" on path: DexPathList[[zip file "/system/framework/android.test.mock.jar", zip file "/system/framework/android.test.runner.jar", zip file "/data/app/app.debug.test-HdSyMEsvYzlt1aceQIeIuw==/base.apk"],nativeLibraryDirectories=[/data/app/app.debug.test-HdSyMEsvYzlt1aceQIeIuw==/lib/arm64, /system/lib64, /vendor/lib64]]
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:134)
at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
... 18 more
我找不到正确的依赖关系,以便在运行时包含EmptyFragmentActivity,因此我的临时解决方法是不使用launchFragmentInContainer
而是启动自己的Activity:
我的测试活动:
class TestFragmentActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
fun replaceFragment(fragment: Fragment) {
supportFragmentManager
.beginTransaction()
.replace(android.R.id.content, fragment)
.commit()
}
}
我的测试:
@RunWith(AndroidJUnit4::class)
class MyFragmentAndroidTest {
@get:Rule val activityRule: ActivityTestRule<TestFragmentActivity> =
ActivityTestRule(TestFragmentActivity::class.java)
@Test
fun test() {
activityRule.activity.replaceFragment(MyFragment.newInstance())
onView(withId(R.id.title)).check(matches(withText("Title"))))
// More assertions.
}
}