#android #unit-testing
#Android #модульное тестирование
Вопрос:
Я получаю следующую ошибку при попытке использовать FragmentScenario в инструментальном тестировании. Я считаю, что проблема связана с типом сборки, поскольку тесты выполняются в режиме «staging» вместо конфигурации сборки «debug».
Любая помощь будет принята с благодарностью.
Ошибка
java.lang.RuntimeException: Unable to resolve activity for: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.example/androidx.fragment.app.testing.FragmentScenario$EmptyFragmentActivity (has extras) }
at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:387)
at androidx.test.core.app.InstrumentationActivityInvoker.startActivity(InstrumentationActivityInvoker.java:416)
at androidx.test.core.app.ActivityScenario.launchInternal(ActivityScenario.java:265)
at androidx.test.core.app.ActivityScenario.launch(ActivityScenario.java:226)
at androidx.fragment.app.testing.FragmentScenario.internalLaunch(FragmentScenario.java:299)
at androidx.fragment.app.testing.FragmentScenario.launchInContainer(FragmentScenario.java:282)
at com.example.view.fragments.MainFragmentTest.testNavigation(MainFragmentTest.kt:139)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at androidx.test.internal.runner.junit4.statement.RunBefores.evaluate(RunBefores.java:80)
at androidx.test.internal.runner.junit4.statement.RunAfters.evaluate(RunAfters.java:61)
at androidx.test.rule.ActivityTestRule$ActivityStatement.evaluate(ActivityTestRule.java:549)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at androidx.test.ext.junit.runners.AndroidJUnit4.run(AndroidJUnit4.java:154)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
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:395)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2189)
Выполняемый тест, который является точной копией документов от Google по тестированию навигации https://developer.android.com/guide/navigation/navigation-testing#kotlin
@Test
fun testNavigation() {
// Create a TestNavHostController
val navController = TestNavHostController(ApplicationProvider.getApplicationContext())
navController.setGraph(R.navigation.nav_graph)
// Create a graphical FragmentScenario for the TitleScreen
// Without adding androidTestImplementation "...fragment-testing" to the build gradle this errors and cannot build. This is because we are in staging variant instead of debug variant.
val fragmentScenario = launchFragmentInContainer<MainFragment>()
// Set the NavController property on the fragment
fragmentScenario.onFragment { fragment ->
Navigation.setViewNavController(fragment.requireView(), navController)
}
// Verify that performing a click changes the NavController’s state
onView(withId(R.id.login)).perform(click())
assertThat(navController.currentDestination?.id.toString(), equalTo(R.id.dashboardFragment.toString()))
}
Манифест
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example">
....
<application
android:name=".Example"
android:allowBackup="true"
android:fullBackupContent="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="${cleartextTrafficPermitted}">
<activity
android:name="com.example.controllers.LauncherActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Launcher"
tools:ignore="LockedOrientationActivity"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.controllers.MainActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize"
android:theme="@style/AppTheme.Launcher"
tools:ignore="LockedOrientationActivity" />
...
</application>
</manifest>
build.gradle(приложение)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'com.google.firebase.crashlytics'
apply plugin: 'com.google.gms.google-services'
apply plugin: "androidx.navigation.safeargs.kotlin"
ext.okhttp_version = '4.7.2'
ext.retrofit_version = '2.9.0'
ext.mockito_version = '3.3.3'
ext.coroutine_version = '1.3.9'
ext.test_version = '1.3.0'
ext.fragment_version = '1.2.5'
ext.nav_version = '2.3.0'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
testBuildType "staging"
defaultConfig {
applicationId "com.example"
minSdkVersion 23
targetSdkVersion 29
versionCode 1
versionName "1.0.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
debug {
buildConfigField "String", "API_DOMAIN", '"https://testing.com"'
// This is needed when API's are connecting to HTTP endpoints. Primary purpose is
// for the staging buildType to use a non-SSL mock server locally.
manifestPlaceholders.cleartextTrafficPermitted ="true"
}
staging {
initWith(buildTypes.debug)
buildConfigField "String", "API_DOMAIN", '"http://127.0.0.1/"'
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField "String", "API_DOMAIN", '"https://production.com"'
// Production version will not need to access HTTP endpoints. Disable
// because it poses a security risk.
manifestPlaceholders.cleartextTrafficPermitted ="false"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
viewBinding {
enabled = true
}
kotlinOptions {
jvmTarget = "1.8"
}
testOptions {
unitTests.includeAndroidResources = true
unitTests.returnDefaultValues = true
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
...
// Test dependencies
testImplementation 'junit:junit:4.13'
testImplementation "org.mockito:mockito-inline:$mockito_version"
testImplementation "org.mockito:mockito-core:$mockito_version"
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "org.mockito:mockito-android:$mockito_version"
androidTestImplementation "androidx.test:core:$test_version"
androidTestImplementation "androidx.test:rules:$test_version"
androidTestImplementation "androidx.test:runner:$test_version"
androidTestImplementation "com.squareup.okhttp3:mockwebserver:$okhttp_version"
androidTestUtil "androidx.test:orchestrator:$test_version"
// Without this, if I change the active build variant to staging I cannot use FragmentScenario as the IDE returns "unresolved reference"
androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
// This is the correct implementation of fragment-testing
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
androidTestImplementation('com.android.support.test.espresso:espresso-contrib:3.0.2') {
// Necessary to avoid version conflicts
exclude group: 'com.android.support', module: 'appcompat'
exclude group: 'com.android.support', module: 'support-v4'
exclude group: 'com.android.support', module: 'support-annotations'
exclude module: 'recyclerview-v7'
}
// Testing Navigation
androidTestImplementation "androidx.navigation:navigation-testing:$nav_version"
...
}
Обновить
Если я удалю оба androidTestImplementation и debugImplementation и заменю на stagingImplementation, чтобы библиотека была доступна для промежуточного варианта сборки, это приведет к сбою всех тестовых классов с «не найдено тестов»
// androidTestImplementation "androidx.fragment:fragment-testing:$fragment_version"
// debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
//noinspection FragmentGradleConfiguration
stagingImplementation "androidx.fragment:fragment-testing:$fragment_version"
Ответ №1:
Если вы не выполняете свои тесты против debug
сборок, debugImplementation
этого недостаточно. Вам нужно добавить:
stagingImplementation "androidx.fragment:fragment-testing:$fragment_version"
Комментарии:
1. Обратите внимание, что проверка Lint, включенная в
fragment-testing
, безусловно, может по-прежнему жаловаться на эту строку — она была построена в предположении, что вы тестируете на свойdebug
тип сборки и может быть безопасно подавлена, если это не относится к вашему конкретному случаю.2. я знаю, я попробовал это, думая, что это так, и сразу почувствовал себя глупо, когда lint пожаловался… Я удалил androidTestImplementation и заменил debug на staging. Я также добавил //noinspection FragmentGradleConfiguration, чтобы lint не жаловался. Теперь я получаю сообщение «Тесты не найдены» при запуске теста. запуск журнала или logcat показывают ошибку.
3. Если он даже не приступает к запуску ваших тестов, это похоже на совершенно отдельную проблему, связанную с вашей конфигурацией Android Studio / run.
4. Когда я комментирую тестовую функцию testNavigation(), includes и stagingImplementation, все тесты выполняются, как ожидалось.
5. Вы хотите сказать, что если ваш тест закомментирован, и простое добавление
stagingImplementation
каким-то образом нарушает работу всей Android Studio и предотвращает запуск всех тестов?
Ответ №2:
В моем случае я использовал
debugImplementation "androidx.fragment:fragment-testing:1.5.0"
и я понизил его до
debugImplementation "androidx.fragment:fragment-testing:1.3.0-alpha08"
Это решило мою проблему, надеюсь, это поможет
Комментарии:
1. Это решило аналогичную проблему