From d7f0ca8ccf23015345ece109e60e846597e15de8 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Apr 2022 11:27:40 +0300 Subject: [PATCH 01/21] Issue#67 Move Project specific Test Tasks out of testoptions.gradle --- build.gradle | 3 ++- gradlescripts/test.tasks.gradle | 16 ++++++++++++++++ gradlescripts/testoptions.gradle | 17 ----------------- 3 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 gradlescripts/test.tasks.gradle diff --git a/build.gradle b/build.gradle index 84e7132..a04b51e 100644 --- a/build.gradle +++ b/build.gradle @@ -33,4 +33,5 @@ apply from: 'gradlescripts/versions.gradle' apply from: 'gradlescripts/detekt.config.gradle' apply from: 'gradlescripts/ktlint.gradle' apply from: 'gradlescripts/lint.gradle' -apply from: 'gradlescripts/testoptions.gradle' \ No newline at end of file +apply from: 'gradlescripts/testoptions.gradle' +apply from: 'gradlescripts/test.tasks.gradle' \ No newline at end of file diff --git a/gradlescripts/test.tasks.gradle b/gradlescripts/test.tasks.gradle new file mode 100644 index 0000000..c8f1744 --- /dev/null +++ b/gradlescripts/test.tasks.gradle @@ -0,0 +1,16 @@ +task jvmTests(dependsOn: ["app:testDebugUnitTest", "core:test", "network:test"]) { + group = 'Tests' + description = 'Run all JVM tests' +} + +task robolectricTests(type: Exec) { + group = 'Tests' + description = 'Run all Robolectric tests based on the Instrumented naming convention' + // todo is there a better way? + commandLine 'sh', './gradlew', 'testDebugUnitTest', '--tests', 'org.fnives.test.*InstrumentedTest' +} + +task androidTests(dependsOn: ["app:connectedDebugAndroidTest"]) { + group = 'Tests' + description = 'Run Android tests' +} \ No newline at end of file diff --git a/gradlescripts/testoptions.gradle b/gradlescripts/testoptions.gradle index ce6575a..7193f48 100644 --- a/gradlescripts/testoptions.gradle +++ b/gradlescripts/testoptions.gradle @@ -62,21 +62,4 @@ subprojects { module -> } } } -} - -task jvmTests(dependsOn: ["app:testDebugUnitTest", "core:test", "network:test"]) { - group = 'Tests' - description = 'Run all JVM tests' -} - -task robolectricTests(type: Exec) { - group = 'Tests' - description = 'Run all Robolectric tests based on the Instrumented naming convention' - // todo is there a better way? - commandLine 'sh', './gradlew', 'testDebugUnitTest', '--tests', 'org.fnives.test.*InstrumentedTest' -} - -task androidTests(dependsOn: ["app:connectedDebugAndroidTest"]) { - group = 'Tests' - description = 'Run Android tests' } \ No newline at end of file From f35fe810e0c7105ee7a0e85d237e61a458eeae73 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Apr 2022 12:53:51 +0300 Subject: [PATCH 02/21] Move dependencies into a single file so it's easier to copy --- app/build.gradle | 40 +------ build.gradle | 3 +- core/build.gradle | 10 +- gradlescripts/testdependencies.gradle | 146 ++++++++++++++++++++++++++ network/build.gradle | 5 +- 5 files changed, 152 insertions(+), 52 deletions(-) create mode 100644 gradlescripts/testdependencies.gradle diff --git a/app/build.gradle b/app/build.gradle index b6beea9..214a08b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -101,46 +101,10 @@ dependencies { implementation project(":core") - testImplementation "androidx.room:room-testing:$androidx_room_version" - testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" - testImplementation "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version" - testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version" - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" - testImplementation "com.jraska.livedata:testing-ktx:$testing_livedata_version" - testImplementation "io.insert-koin:koin-test-junit5:$koin_version" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + applyAppTestDependenciesTo(this) + applyComposeTestDependenciesTo(this) - // robolectric specific - testImplementation "junit:junit:$testing_junit4_version" - testImplementation "org.robolectric:robolectric:$testing_robolectric_version" - testImplementation "androidx.test:core:$testing_androidx_code_version" - testImplementation "androidx.test:runner:$testing_androidx_code_version" - testImplementation "androidx.test.ext:junit:$testing_androidx_junit_version" - testImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version" - testImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version" - testImplementation "androidx.test.espresso:espresso-contrib:$testing_espresso_version" - testImplementation project(':mockserver') - testImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version" - testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version" - - androidTestImplementation "androidx.room:room-testing:$androidx_room_version" - androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" - androidTestImplementation "io.insert-koin:koin-test-junit4:$koin_version" - androidTestImplementation "junit:junit:$testing_junit4_version" - androidTestImplementation "androidx.test:core:$testing_androidx_code_version" - androidTestImplementation "androidx.test:runner:$testing_androidx_code_version" - androidTestImplementation "androidx.test.ext:junit:$testing_androidx_junit_version" - androidTestImplementation "androidx.test.espresso:espresso-core:$testing_espresso_version" - androidTestImplementation "androidx.test.espresso:espresso-intents:$testing_espresso_version" - androidTestImplementation "androidx.test.espresso:espresso-contrib:$testing_espresso_version" - androidTestImplementation "androidx.compose.ui:ui-test-junit4:$androidx_compose_version" - testImplementation "androidx.compose.ui:ui-test-junit4:$androidx_compose_version" - debugImplementation "androidx.compose.ui:ui-test-manifest:$androidx_compose_version" androidTestImplementation project(':mockserver') - androidTestImplementation "androidx.arch.core:core-testing:$testing_androidx_arch_core_version" - androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version" - - implementation "io.reactivex.rxjava3:rxjava:3.1.4" testImplementation testFixtures(project(':core')) androidTestImplementation testFixtures(project(':core')) diff --git a/build.gradle b/build.gradle index a04b51e..0e88155 100644 --- a/build.gradle +++ b/build.gradle @@ -34,4 +34,5 @@ apply from: 'gradlescripts/detekt.config.gradle' apply from: 'gradlescripts/ktlint.gradle' apply from: 'gradlescripts/lint.gradle' apply from: 'gradlescripts/testoptions.gradle' -apply from: 'gradlescripts/test.tasks.gradle' \ No newline at end of file +apply from: 'gradlescripts/test.tasks.gradle' +apply from: 'gradlescripts/testdependencies.gradle' \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 617bb40..ebc50e5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -20,15 +20,7 @@ dependencies { api project(":model") implementation project(":network") - testImplementation "io.insert-koin:koin-test-junit5:$koin_version" - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" - testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version" - testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" - testImplementation "com.squareup.retrofit2:retrofit:$retrofit_version" - testImplementation "app.cash.turbine:turbine:$turbine_version" - - testImplementation "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version" + applyCoreTestDependenciesTo(this) testImplementation project(':mockserver') testFixturesApi testFixtures(project(':network')) diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle new file mode 100644 index 0000000..428a4cd --- /dev/null +++ b/gradlescripts/testdependencies.gradle @@ -0,0 +1,146 @@ +project.ext { + + /* + ------------------USAGE------------------ + add line in root build.gradle: + apply from: 'gradlescripts/testdependencies.gradle' (this file) + then in your modules you can use: + + ------------------NETWORK(Java Module)------------------ + dependencies { + applyNetworkTestDependenciesTo(this) + } + + ------------------CORE(Java Module)------------------ + dependencies { + applyCoreTestDependenciesTo(this) + } + + ------------------APP(Android Module)------------------ + dependencies { + applyAppTestDependenciesTo(this) + applyComposeTestDependenciesTo(this) // if you are using compose + } + + ------------------VERSIONS------------------ + versions try to get the global value, if not found they fall back to some defaults. + You can find them just below + */ + + def propertyOrNull = { key -> + if (hasProperty(key)) { + return property(key) + } else { + return null + } + } + + // ------------------VERSIONS------------------ + def testing_junit5_version = propertyOrNull('junit5_version') ?: "5.7.0" + def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.13.2" + def testing_robolectric_version = propertyOrNull('robolectric_version') ?: "4.7" + def testing_androidx_code_version = propertyOrNull('androidx_test_version') ?: "1.4.0" + def testing_androidx_junit_version = propertyOrNull('androidx_junit_version') ?: "1.1.3" + def testing_espresso_version = propertyOrNull('espresso_version') ?: "3.4.0" + def testing_androidx_arch_core_version = propertyOrNull('androidx_arch_version') ?: "2.1.0" + def test_coroutines_version = propertyOrNull('coroutines_versionx') ?: "1.6.0" + def testing_kotlin_mockito_version = propertyOrNull('mockito_version') ?: "3.1.0" + def testing_koin_version = propertyOrNull('koin_version') ?: "3.1.2" + def testing_json_assert_version = propertyOrNull('json_assert_version') ?: "1.5.0" + def testing_okhttp3 = propertyOrNull('okhttp_version') ?: "4.9.3" + def testing_turbine_version = propertyOrNull('turbine_version') ?: "0.7.0" + def testing_androidx_room_version = propertyOrNull('room_version') ?: "2.4.1" + def testing_livedata_version = propertyOrNull('testing_livedata_version') ?: "1.2.0" + def testing_compose_version = propertyOrNull('compose_version') ?: "1.1.0" + + // ------------------PRIVATE------------------ + // JUni4 + Espresso + Room + def androidSpecificTestDependencies = [ + "junit:junit:$testing_junit4_version", + + "androidx.room:room-testing:$testing_androidx_room_version", + + "com.jraska.livedata:testing-ktx:$testing_livedata_version", + + "androidx.test:core:$testing_androidx_code_version", + "androidx.test:runner:$testing_androidx_code_version", + + "androidx.test.ext:junit:$testing_androidx_junit_version", + + "androidx.test.espresso:espresso-core:$testing_espresso_version", + "androidx.test.espresso:espresso-intents:$testing_espresso_version", + "androidx.test.espresso:espresso-contrib:$testing_espresso_version", + + "androidx.arch.core:core-testing:$testing_androidx_arch_core_version", + ] + + // ------------------PRIVATE------------------ + def applyStandardTestDependenciesTo = { module -> + module.dependencies { + // coroutine testing + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version" + + // mockito, mocking library + testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version" + + testImplementation "io.insert-koin:koin-test-junit5:$testing_koin_version" + // junit5 + testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + testImplementation "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + } + } + + // ------------------NETWORK------------------ + applyNetworkTestDependenciesTo = { module -> + applyStandardTestDependenciesTo(module) + + module.dependencies { + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + // JSON Assert + testImplementation "org.skyscreamer:jsonassert:$testing_json_assert_version" + + // mockwebserver + https support + testImplementation "com.squareup.okhttp3:mockwebserver:$testing_okhttp3" + testImplementation "com.squareup.okhttp3:okhttp-tls:$testing_okhttp3" + } + } + + // ------------------CORE------------------ + applyCoreTestDependenciesTo = { module -> + applyStandardTestDependenciesTo(module) + + module.dependencies { + // turbine, flow testing + testImplementation "app.cash.turbine:turbine:$testing_turbine_version" + } + } + + // ------------------APP------------------ + applyAppTestDependenciesTo = { module -> + applyStandardTestDependenciesTo(module) + + module.dependencies { + testImplementation "org.robolectric:robolectric:$testing_robolectric_version" + testRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version" + + androidSpecificTestDependencies.forEach { dependency -> + testImplementation dependency + androidTestImplementation dependency + } + + androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version" + androidTestImplementation "io.insert-koin:koin-test-junit5:$testing_koin_version" + androidTestRuntimeOnly "org.junit.vintage:junit-vintage-engine:$testing_junit5_version" + } + } + + // ------------------COMPOSE------------------ + applyComposeTestDependenciesTo = { module -> + module.dependencies { + testImplementation "androidx.compose.ui:ui-test-junit4:$testing_compose_version" + androidTestImplementation "androidx.compose.ui:ui-test-junit4:$testing_compose_version" + debugImplementation "androidx.compose.ui:ui-test-manifest:$testing_compose_version" + } + } +} \ No newline at end of file diff --git a/network/build.gradle b/network/build.gradle index e8cf19c..ed5e6f7 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -24,10 +24,7 @@ dependencies { api project(":model") - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" - testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version" - testImplementation "org.skyscreamer:jsonassert:$testing_json_assert_version" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + applyNetworkTestDependenciesTo(this) testFixturesApi project(':mockserver') testFixturesApi "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" From 4932b4b2e084c467eb91d15b94e7f20cae1c5ea7 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Apr 2022 14:37:39 +0300 Subject: [PATCH 03/21] Issue#67 Extract MigrationTestHelper into separate module --- app/build.gradle | 9 ++- .../AndroidMigrationTestRuleFactory.kt | 41 ---------- .../SpecificTestConfigurationsFactory.kt | 7 -- .../RobolectricMigrationTestHelperFactory.kt | 41 ---------- .../SpecificTestConfigurationsFactory.kt | 7 -- .../MigrationToLatestInstrumentedTest.kt | 10 +-- .../configuration/SharedMigrationTestRule.kt | 60 -------------- .../SharedMigrationTestRuleFactory.kt | 27 ------- .../SnackbarVerificationHelper.kt | 8 +- .../TestConfigurationsFactory.kt | 12 --- .../showcase/ui/home/MainViewModelTest.kt | 2 +- gradlescripts/testdependencies.gradle | 6 +- gradlescripts/versions.gradle | 6 +- settings.gradle | 4 +- test-util-shared-android/.gitignore | 1 + test-util-shared-android/build.gradle | 36 +++++++++ test-util-shared-android/consumer-rules.pro | 0 test-util-shared-android/proguard-rules.pro | 21 +++++ .../src/main/AndroidManifest.xml | 5 ++ .../testutil}/AndroidMigrationTestRule.kt | 8 +- test-util-shared-robolectric/.gitignore | 1 + test-util-shared-robolectric/build.gradle | 35 ++++++++ .../src/main/AndroidManifest.xml | 5 ++ .../testutil/SharedMigrationTestRule.kt | 29 +++++++ .../SharedMigrationTestRuleFactory.kt | 79 +++++++++++++++++++ .../RobolectricMigrationTestRule.java | 14 ++-- 26 files changed, 249 insertions(+), 225 deletions(-) delete mode 100644 app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRuleFactory.kt delete mode 100644 app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt delete mode 100644 app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelperFactory.kt delete mode 100644 app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRule.kt delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRuleFactory.kt delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt create mode 100644 test-util-shared-android/.gitignore create mode 100644 test-util-shared-android/build.gradle create mode 100644 test-util-shared-android/consumer-rules.pro create mode 100644 test-util-shared-android/proguard-rules.pro create mode 100644 test-util-shared-android/src/main/AndroidManifest.xml rename {app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration => test-util-shared-android/src/main/java/org/fnives/test/showcase/android/testutil}/AndroidMigrationTestRule.kt (88%) create mode 100644 test-util-shared-robolectric/.gitignore create mode 100644 test-util-shared-robolectric/build.gradle create mode 100644 test-util-shared-robolectric/src/main/AndroidManifest.xml create mode 100644 test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt create mode 100644 test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt rename app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelper.java => test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/robolectric/RobolectricMigrationTestRule.java (97%) diff --git a/app/build.gradle b/app/build.gradle index 214a08b..e7faf43 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -92,9 +92,9 @@ dependencies { implementation "io.insert-koin:koin-android:$koin_version" implementation "io.insert-koin:koin-androidx-compose:$koin_version" - implementation "androidx.room:room-runtime:$androidx_room_version" - kapt "androidx.room:room-compiler:$androidx_room_version" - implementation "androidx.room:room-ktx:$androidx_room_version" + implementation "androidx.room:room-runtime:$room_version" + kapt "androidx.room:room-compiler:$room_version" + implementation "androidx.room:room-ktx:$room_version" implementation "io.coil-kt:coil:$coil_version" implementation "io.coil-kt:coil-compose:$coil_version" @@ -108,5 +108,8 @@ dependencies { testImplementation testFixtures(project(':core')) androidTestImplementation testFixtures(project(':core')) + implementation project(':test-util-shared-robolectric') + androidTestImplementation project(':test-util-shared-android') + } diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRuleFactory.kt b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRuleFactory.kt deleted file mode 100644 index b4c5395..0000000 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRuleFactory.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -import android.app.Instrumentation -import androidx.room.RoomDatabase -import androidx.room.migration.AutoMigrationSpec -import androidx.sqlite.db.SupportSQLiteOpenHelper - -object AndroidMigrationTestRuleFactory : SharedMigrationTestRuleFactory { - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class - ): SharedMigrationTestRule = - AndroidMigrationTestRule( - instrumentation, - databaseClass - ) - - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List - ): SharedMigrationTestRule = - AndroidMigrationTestRule( - instrumentation, - databaseClass, - specs - ) - - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List, - openFactory: SupportSQLiteOpenHelper.Factory - ): SharedMigrationTestRule = - AndroidMigrationTestRule( - instrumentation, - databaseClass, - specs, - openFactory, - ) -} diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt b/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt deleted file mode 100644 index 974ff95..0000000 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -object SpecificTestConfigurationsFactory : TestConfigurationsFactory { - - override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory = - AndroidMigrationTestRuleFactory -} diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelperFactory.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelperFactory.kt deleted file mode 100644 index 887a7b9..0000000 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelperFactory.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -import android.app.Instrumentation -import androidx.room.RoomDatabase -import androidx.room.migration.AutoMigrationSpec -import androidx.sqlite.db.SupportSQLiteOpenHelper - -object RobolectricMigrationTestHelperFactory : SharedMigrationTestRuleFactory { - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class - ): SharedMigrationTestRule = - RobolectricMigrationTestHelper( - instrumentation, - databaseClass - ) - - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List - ): SharedMigrationTestRule = - RobolectricMigrationTestHelper( - instrumentation, - databaseClass, - specs - ) - - override fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List, - openFactory: SupportSQLiteOpenHelper.Factory - ): SharedMigrationTestRule = - RobolectricMigrationTestHelper( - instrumentation, - databaseClass, - specs, - openFactory - ) -} diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt deleted file mode 100644 index e9c281d..0000000 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/SpecificTestConfigurationsFactory.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -object SpecificTestConfigurationsFactory : TestConfigurationsFactory { - - override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory = - RobolectricMigrationTestHelperFactory -} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/storage/migration/MigrationToLatestInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/storage/migration/MigrationToLatestInstrumentedTest.kt index d615a49..a2527aa 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/storage/migration/MigrationToLatestInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/storage/migration/MigrationToLatestInstrumentedTest.kt @@ -1,16 +1,14 @@ package org.fnives.test.showcase.storage.migration import androidx.room.Room -import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking +import org.fnives.test.showcase.android.testutil.SharedMigrationTestRule import org.fnives.test.showcase.storage.LocalDatabase import org.fnives.test.showcase.storage.favourite.FavouriteEntity import org.fnives.test.showcase.storage.migation.Migration1To2 -import org.fnives.test.showcase.testutils.configuration.SharedMigrationTestRule -import org.fnives.test.showcase.testutils.configuration.createSharedMigrationTestRule import org.junit.After import org.junit.Assert import org.junit.Rule @@ -28,11 +26,7 @@ import java.io.IOException class MigrationToLatestInstrumentedTest { @get:Rule - val helper: SharedMigrationTestRule = createSharedMigrationTestRule( - InstrumentationRegistry.getInstrumentation(), - emptyList(), - FrameworkSQLiteOpenHelperFactory() - ) + val helper = SharedMigrationTestRule(instrumentation = InstrumentationRegistry.getInstrumentation()) private fun getMigratedRoomDatabase(): LocalDatabase { val database: LocalDatabase = Room.databaseBuilder( diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRule.kt deleted file mode 100644 index f521053..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRule.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -import android.app.Instrumentation -import androidx.room.RoomDatabase -import androidx.room.migration.AutoMigrationSpec -import androidx.room.migration.Migration -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.sqlite.db.SupportSQLiteOpenHelper -import org.junit.rules.TestRule -import java.io.IOException - -interface SharedMigrationTestRule : TestRule { - - @Throws(IOException::class) - fun createDatabase(name: String, version: Int): SupportSQLiteDatabase - - @Throws(IOException::class) - fun runMigrationsAndValidate( - name: String, - version: Int, - validateDroppedTables: Boolean, - vararg migrations: Migration - ): SupportSQLiteDatabase - - fun closeWhenFinished(db: SupportSQLiteDatabase) - fun closeWhenFinished(db: RoomDatabase) -} - -inline fun createSharedMigrationTestRule( - instrumentation: Instrumentation -): SharedMigrationTestRule = - SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory() - .createSharedMigrationTestRule( - instrumentation, - DB::class.java - ) - -inline fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - specs: List -): SharedMigrationTestRule = - SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory() - .createSharedMigrationTestRule( - instrumentation, - DB::class.java, - specs - ) - -inline fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - specs: List, - openFactory: SupportSQLiteOpenHelper.Factory -): SharedMigrationTestRule = - SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory() - .createSharedMigrationTestRule( - instrumentation, - DB::class.java, - specs, - openFactory - ) diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRuleFactory.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRuleFactory.kt deleted file mode 100644 index c99713e..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SharedMigrationTestRuleFactory.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -import android.app.Instrumentation -import androidx.room.RoomDatabase -import androidx.room.migration.AutoMigrationSpec -import androidx.sqlite.db.SupportSQLiteOpenHelper - -interface SharedMigrationTestRuleFactory { - - fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - ): SharedMigrationTestRule - - fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List - ): SharedMigrationTestRule - - fun createSharedMigrationTestRule( - instrumentation: Instrumentation, - databaseClass: Class, - specs: List, - openFactory: SupportSQLiteOpenHelper.Factory - ): SharedMigrationTestRule -} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt index cdba01c..3827bd7 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt @@ -15,11 +15,13 @@ import org.hamcrest.Matchers class SnackbarVerificationHelper { - fun assertIsShownWithText(@StringRes stringResID: Int) { + fun assertIsShownWithText(@StringRes stringResID: Int, doDismiss: Boolean = true) { Espresso.onView(ViewMatchers.withId(R.id.snackbar_text)) .check(ViewAssertions.matches(ViewMatchers.withText(stringResID))) - Espresso.onView(ViewMatchers.isAssignableFrom(Snackbar.SnackbarLayout::class.java)).perform(ViewActions.swipeRight()) - Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainUntilSnackbarDismissed()) + if (doDismiss) { + Espresso.onView(ViewMatchers.isAssignableFrom(Snackbar.SnackbarLayout::class.java)).perform(ViewActions.swipeRight()) + Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainUntilSnackbarDismissed()) + } } fun assertIsNotShown() { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt deleted file mode 100644 index 18336c9..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/TestConfigurationsFactory.kt +++ /dev/null @@ -1,12 +0,0 @@ -package org.fnives.test.showcase.testutils.configuration - -/** - * Defines the platform specific configurations for Robolectric and AndroidTest. - * - * Each should have an object [SpecificTestConfigurationsFactory] implementing this interface so the SharedTests are - * configured properly. - */ -interface TestConfigurationsFactory { - - fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory -} diff --git a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt index 6ce1a3c..e0ca3e8 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt @@ -20,11 +20,11 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith +import org.mockito.Mockito.verifyNoInteractions import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify -import org.mockito.kotlin.verifyNoInteractions import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle index 428a4cd..991af51 100644 --- a/gradlescripts/testdependencies.gradle +++ b/gradlescripts/testdependencies.gradle @@ -37,12 +37,12 @@ project.ext { // ------------------VERSIONS------------------ def testing_junit5_version = propertyOrNull('junit5_version') ?: "5.7.0" - def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.13.2" + def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.12" def testing_robolectric_version = propertyOrNull('robolectric_version') ?: "4.7" def testing_androidx_code_version = propertyOrNull('androidx_test_version') ?: "1.4.0" def testing_androidx_junit_version = propertyOrNull('androidx_junit_version') ?: "1.1.3" def testing_espresso_version = propertyOrNull('espresso_version') ?: "3.4.0" - def testing_androidx_arch_core_version = propertyOrNull('androidx_arch_version') ?: "2.1.0" + def testing_androidx_arch_core_version = propertyOrNull('arch_core_version') ?: "2.1.0" def test_coroutines_version = propertyOrNull('coroutines_versionx') ?: "1.6.0" def testing_kotlin_mockito_version = propertyOrNull('mockito_version') ?: "3.1.0" def testing_koin_version = propertyOrNull('koin_version') ?: "3.1.2" @@ -52,6 +52,7 @@ project.ext { def testing_androidx_room_version = propertyOrNull('room_version') ?: "2.4.1" def testing_livedata_version = propertyOrNull('testing_livedata_version') ?: "1.2.0" def testing_compose_version = propertyOrNull('compose_version') ?: "1.1.0" + def testing_hamcrest_version = propertyOrNull('hamcrest_version')?: "2.2" // ------------------PRIVATE------------------ // JUni4 + Espresso + Room @@ -70,6 +71,7 @@ project.ext { "androidx.test.espresso:espresso-core:$testing_espresso_version", "androidx.test.espresso:espresso-intents:$testing_espresso_version", "androidx.test.espresso:espresso-contrib:$testing_espresso_version", + "org.hamcrest:hamcrest:$testing_hamcrest_version", "androidx.arch.core:core-testing:$testing_androidx_arch_core_version", ] diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index 3732bda..a75af32 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -5,7 +5,7 @@ project.ext { androidx_constraintlayout_version = "2.1.3" androidx_livedata_version = "2.4.0" androidx_swiperefreshlayout_version = "1.1.0" - androidx_room_version = "2.4.1" + room_version = "2.4.1" activity_ktx_version = "1.4.0" androidx_navigation = "2.4.0" @@ -23,12 +23,12 @@ project.ext { testing_androidx_code_version = "1.4.0" testing_androidx_junit_version = "1.1.3" - testing_androidx_arch_core_version = "2.1.0" + arch_core_version = "2.1.0" testing_livedata_version = "1.2.0" testing_kotlin_mockito_version = "4.0.0" testing_junit5_version = "5.7.0" testing_json_assert_version = "1.5.0" - testing_junit4_version = "4.13.2" + testing_junit4_version = "4.12" testing_robolectric_version = "4.7" testing_espresso_version = "3.4.0" } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index a7f91d5..1b941c4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,6 +1,8 @@ +rootProject.name = "TestShowCase" include ':mockserver' include ':model' include ':core' include ':network' include ':app' -rootProject.name = "TestShowCase" \ No newline at end of file +include ':test-util-shared-android' +include ':test-util-shared-robolectric' diff --git a/test-util-shared-android/.gitignore b/test-util-shared-android/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/test-util-shared-android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-util-shared-android/build.gradle b/test-util-shared-android/build.gradle new file mode 100644 index 0000000..bf17210 --- /dev/null +++ b/test-util-shared-android/build.gradle @@ -0,0 +1,36 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation "androidx.room:room-testing:$room_version" + api project(':test-util-shared-robolectric') + +} \ No newline at end of file diff --git a/test-util-shared-android/consumer-rules.pro b/test-util-shared-android/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/test-util-shared-android/proguard-rules.pro b/test-util-shared-android/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/test-util-shared-android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/test-util-shared-android/src/main/AndroidManifest.xml b/test-util-shared-android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..80e5dbb --- /dev/null +++ b/test-util-shared-android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRule.kt b/test-util-shared-android/src/main/java/org/fnives/test/showcase/android/testutil/AndroidMigrationTestRule.kt similarity index 88% rename from app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRule.kt rename to test-util-shared-android/src/main/java/org/fnives/test/showcase/android/testutil/AndroidMigrationTestRule.kt index 882b3eb..33d0a0a 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/testutils/configuration/AndroidMigrationTestRule.kt +++ b/test-util-shared-android/src/main/java/org/fnives/test/showcase/android/testutil/AndroidMigrationTestRule.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.configuration +package org.fnives.test.showcase.android.testutil import android.app.Instrumentation import androidx.room.RoomDatabase @@ -10,6 +10,9 @@ import androidx.sqlite.db.SupportSQLiteOpenHelper import org.junit.runner.Description import org.junit.runners.model.Statement +/** + * Wrapper around [MigrationTestHelper] so it can be created in SharedTests, in both Robolectric and on Device. + */ class AndroidMigrationTestRule : SharedMigrationTestRule { private val migrationTestHelper: MigrationTestHelper @@ -35,8 +38,7 @@ class AndroidMigrationTestRule : SharedMigrationTestRule { specs: List, openFactory: SupportSQLiteOpenHelper.Factory ) { - migrationTestHelper = - MigrationTestHelper(instrumentation, databaseClass, specs, openFactory) + migrationTestHelper = MigrationTestHelper(instrumentation, databaseClass, specs, openFactory) } override fun apply(base: Statement, description: Description): Statement = diff --git a/test-util-shared-robolectric/.gitignore b/test-util-shared-robolectric/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/test-util-shared-robolectric/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-util-shared-robolectric/build.gradle b/test-util-shared-robolectric/build.gradle new file mode 100644 index 0000000..ed8dde6 --- /dev/null +++ b/test-util-shared-robolectric/build.gradle @@ -0,0 +1,35 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation "androidx.room:room-testing:$room_version" + implementation "androidx.arch.core:core-testing:$arch_core_version" +} \ No newline at end of file diff --git a/test-util-shared-robolectric/src/main/AndroidManifest.xml b/test-util-shared-robolectric/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ed38c52 --- /dev/null +++ b/test-util-shared-robolectric/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt new file mode 100644 index 0000000..b35c199 --- /dev/null +++ b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt @@ -0,0 +1,29 @@ +package org.fnives.test.showcase.android.testutil + +import androidx.room.RoomDatabase +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import org.junit.rules.TestRule +import java.io.IOException + +/** + * Unifying API above [MigrationTestHelper][androidx.room.testing.MigrationTestHelper]. + * + * This is intended to be used in MigrationTests that are shared. Meaning the same test can run on both Device and via Robolectric. +*/ +interface SharedMigrationTestRule : TestRule { + + @Throws(IOException::class) + fun createDatabase(name: String, version: Int): SupportSQLiteDatabase + + @Throws(IOException::class) + fun runMigrationsAndValidate( + name: String, + version: Int, + validateDroppedTables: Boolean, + vararg migrations: Migration + ): SupportSQLiteDatabase + + fun closeWhenFinished(db: SupportSQLiteDatabase) + fun closeWhenFinished(db: RoomDatabase) +} \ No newline at end of file diff --git a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt new file mode 100644 index 0000000..2e3db83 --- /dev/null +++ b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt @@ -0,0 +1,79 @@ +package org.fnives.test.showcase.android.testutil + +import android.app.Instrumentation +import androidx.room.RoomDatabase +import androidx.room.migration.AutoMigrationSpec +import androidx.sqlite.db.SupportSQLiteOpenHelper +import org.fnives.test.showcase.android.testutil.robolectric.RobolectricMigrationTestRule + +inline fun SharedMigrationTestRule( + instrumentation: Instrumentation +): SharedMigrationTestRule = + createAndroidClassOrRobolectric( + androidClassFactory = { androidClass -> + val constructor = androidClass.getConstructor( + Instrumentation::class.java, + Class::class.java + ) + constructor.newInstance(instrumentation, Database::class.java) as SharedMigrationTestRule + }, + robolectricFactory = { + RobolectricMigrationTestRule(instrumentation, Database::class.java) + } + ) + +inline fun SharedMigrationTestRule( + instrumentation: Instrumentation, + specs: List +): SharedMigrationTestRule = + createAndroidClassOrRobolectric( + androidClassFactory = { androidClass -> + val constructor = androidClass.getConstructor( + Instrumentation::class.java, + Class::class.java, + List::class.java + ) + constructor.newInstance(instrumentation, Database::class.java, specs) as SharedMigrationTestRule + }, + robolectricFactory = { + RobolectricMigrationTestRule(instrumentation, Database::class.java, specs) + } + ) + +inline fun SharedMigrationTestRule( + instrumentation: Instrumentation, + specs: List, + openFactory: SupportSQLiteOpenHelper.Factory +): SharedMigrationTestRule = + createAndroidClassOrRobolectric( + androidClassFactory = { androidClass -> + val constructor = androidClass.getConstructor( + Instrumentation::class.java, + Class::class.java, + List::class.java, + SupportSQLiteOpenHelper.Factory::class.java + ) + constructor.newInstance(instrumentation, Database::class.java, specs, openFactory) as SharedMigrationTestRule + }, + robolectricFactory = { + RobolectricMigrationTestRule(instrumentation, Database::class.java, specs, openFactory) + } + ) + +fun createAndroidClassOrRobolectric( + androidClassFactory: (Class<*>) -> Any, + robolectricFactory: () -> SharedMigrationTestRule +): SharedMigrationTestRule { + val androidClass = getAndroidClass() + return if (androidClass == null) { + robolectricFactory() + } else { + androidClassFactory(androidClass) as SharedMigrationTestRule + } +} + +private fun getAndroidClass() = try { + Class.forName("org.fnives.test.showcase.android.testutil.AndroidMigrationTestRule") +} catch (classNotFoundException: ClassNotFoundException) { + null +} \ No newline at end of file diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelper.java b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/robolectric/RobolectricMigrationTestRule.java similarity index 97% rename from app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelper.java rename to test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/robolectric/RobolectricMigrationTestRule.java index daf10f3..855d13a 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/testutils/configuration/RobolectricMigrationTestHelper.java +++ b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/robolectric/RobolectricMigrationTestRule.java @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.configuration; +package org.fnives.test.showcase.android.testutil.robolectric; import android.annotation.SuppressLint; import android.app.Instrumentation; @@ -32,6 +32,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase; import androidx.sqlite.db.SupportSQLiteOpenHelper; import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory; +import org.fnives.test.showcase.android.testutil.SharedMigrationTestRule; import org.jetbrains.annotations.NotNull; import org.junit.rules.TestWatcher; import org.junit.runner.Description; @@ -56,8 +57,9 @@ import java.util.Set; * * reference: https://github.com/robolectric/robolectric/issues/2065 */ -public class RobolectricMigrationTestHelper extends TestWatcher implements SharedMigrationTestRule { - private static final String TAG = "RobolectricMigrationTestHelper"; +@SuppressLint("RestrictedApi") +public class RobolectricMigrationTestRule extends TestWatcher implements SharedMigrationTestRule { + private static final String TAG = "RobolectricMigrationTR"; private final String mAssetsFolder; private final SupportSQLiteOpenHelper.Factory mOpenFactory; private List> mManagedDatabases = new ArrayList<>(); @@ -77,7 +79,7 @@ public class RobolectricMigrationTestHelper extends TestWatcher implements Share * @param instrumentation The instrumentation instance. * @param databaseClass The Database class to be tested. */ - public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation, + public RobolectricMigrationTestRule(@NonNull Instrumentation instrumentation, @NonNull Class databaseClass) { this(instrumentation, databaseClass, new ArrayList<>(), new FrameworkSQLiteOpenHelperFactory()); @@ -96,7 +98,7 @@ public class RobolectricMigrationTestHelper extends TestWatcher implements Share * @param specs The list of available auto migration specs that will be provided to * Room at runtime. */ - public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation, + public RobolectricMigrationTestRule(@NonNull Instrumentation instrumentation, @NonNull Class databaseClass, @NonNull List specs) { this(instrumentation, databaseClass, specs, new FrameworkSQLiteOpenHelperFactory()); @@ -116,7 +118,7 @@ public class RobolectricMigrationTestHelper extends TestWatcher implements Share * Room at runtime. * @param openFactory Factory class that allows creation of {@link SupportSQLiteOpenHelper} */ - public RobolectricMigrationTestHelper(@NonNull Instrumentation instrumentation, + public RobolectricMigrationTestRule(@NonNull Instrumentation instrumentation, @NonNull Class databaseClass, @NonNull List specs, @NonNull SupportSQLiteOpenHelper.Factory openFactory From 689aee9702d42749df924ddf71e0434afad76f05 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 15:08:42 +0300 Subject: [PATCH 04/21] Issue#67 Extract runOnUIAwaitOnCurrent into separate module --- app/build.gradle | 3 +- .../testutils/idling/DispatcherTestRule.kt | 2 +- .../idling/MainDispatcherTestRule.kt | 2 +- .../testutils/viewactions/PullToRefresh.kt | 2 +- gradlescripts/testdependencies.gradle | 2 +- settings.gradle | 1 + test-util-android/.gitignore | 1 + test-util-android/build.gradle | 34 +++++++++++++++++++ test-util-android/consumer-rules.pro | 0 test-util-android/proguard-rules.pro | 21 ++++++++++++ .../src/main/AndroidManifest.xml | 5 +++ .../doBlockinglyOnMainThread.kt | 7 +++- 12 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 test-util-android/.gitignore create mode 100644 test-util-android/build.gradle create mode 100644 test-util-android/consumer-rules.pro create mode 100644 test-util-android/proguard-rules.pro create mode 100644 test-util-android/src/main/AndroidManifest.xml rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization}/doBlockinglyOnMainThread.kt (69%) diff --git a/app/build.gradle b/app/build.gradle index e7faf43..37ae54c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,6 +110,7 @@ dependencies { androidTestImplementation testFixtures(project(':core')) implementation project(':test-util-shared-robolectric') androidTestImplementation project(':test-util-shared-android') - + implementation project(':test-util-android') + androidTestImplementation project(':test-util-android') } diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt index 8d2f0bc..26a7d43 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt @@ -3,7 +3,7 @@ package org.fnives.test.showcase.testutils.idling import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher -import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent +import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.junit.rules.TestRule import org.junit.runner.Description diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt index de21704..59a1fb9 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain -import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent +import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.junit.rules.TestRule import org.junit.runner.Description diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt index fa8a14c..d1b4751 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt @@ -5,7 +5,7 @@ import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.listener import androidx.test.espresso.UiController import androidx.test.espresso.ViewAction -import org.fnives.test.showcase.testutils.runOnUIAwaitOnCurrent +import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.hamcrest.BaseMatcher import org.hamcrest.CoreMatchers.isA import org.hamcrest.Description diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle index 991af51..c43b981 100644 --- a/gradlescripts/testdependencies.gradle +++ b/gradlescripts/testdependencies.gradle @@ -43,7 +43,7 @@ project.ext { def testing_androidx_junit_version = propertyOrNull('androidx_junit_version') ?: "1.1.3" def testing_espresso_version = propertyOrNull('espresso_version') ?: "3.4.0" def testing_androidx_arch_core_version = propertyOrNull('arch_core_version') ?: "2.1.0" - def test_coroutines_version = propertyOrNull('coroutines_versionx') ?: "1.6.0" + def test_coroutines_version = propertyOrNull('coroutines_version') ?: "1.6.0" def testing_kotlin_mockito_version = propertyOrNull('mockito_version') ?: "3.1.0" def testing_koin_version = propertyOrNull('koin_version') ?: "3.1.2" def testing_json_assert_version = propertyOrNull('json_assert_version') ?: "1.5.0" diff --git a/settings.gradle b/settings.gradle index 1b941c4..7e8bd01 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,3 +6,4 @@ include ':network' include ':app' include ':test-util-shared-android' include ':test-util-shared-robolectric' +include ':test-util-android' diff --git a/test-util-android/.gitignore b/test-util-android/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/test-util-android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle new file mode 100644 index 0000000..83ea82d --- /dev/null +++ b/test-util-android/build.gradle @@ -0,0 +1,34 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" +} \ No newline at end of file diff --git a/test-util-android/consumer-rules.pro b/test-util-android/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/test-util-android/proguard-rules.pro b/test-util-android/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/test-util-android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/test-util-android/src/main/AndroidManifest.xml b/test-util-android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..80e5dbb --- /dev/null +++ b/test-util-android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt similarity index 69% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt index f8a8e91..5df6bd8 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/doBlockinglyOnMainThread.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt @@ -1,10 +1,15 @@ -package org.fnives.test.showcase.testutils +package org.fnives.test.showcase.android.testutil.synchronization import android.os.Handler import android.os.Looper import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking +/** + * Runs the given action on the MainThread and blocks currentThread, until it is completed. + * + * It is safe to call this from the MainThread. + */ fun runOnUIAwaitOnCurrent(action: () -> Unit) { if (Looper.myLooper() === Looper.getMainLooper()) { action() From 1c0153db75758a4abe26d96769c6ce74ee18c924 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 15:12:59 +0300 Subject: [PATCH 05/21] Issue#67 Extract ActivityScenario.safeClose into separate module --- .../ui/RobolectricAuthActivityInstrumentedTest.kt | 2 +- .../testutils/statesetup/SetupAuthenticationState.kt | 2 +- .../showcase/ui/home/MainActivityInstrumentedTest.kt | 2 +- .../showcase/ui/login/AuthActivityInstrumentedTest.kt | 2 +- .../ui/splash/SplashActivityInstrumentedTest.kt | 2 +- gradlescripts/versions.gradle | 2 +- test-util-android/build.gradle | 1 + .../testutil/activity}/ActivityScenarioExtensions.kt | 11 ++++++++--- 8 files changed, 15 insertions(+), 9 deletions(-) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/activity}/ActivityScenarioExtensions.kt (61%) diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt index 01e6399..f66736f 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt @@ -19,7 +19,7 @@ import org.fnives.test.showcase.testutils.idling.Disposable import org.fnives.test.showcase.testutils.idling.IdlingResourceDisposable import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule.Companion.advanceUntilIdleWithIdlingResources import org.fnives.test.showcase.testutils.idling.OkHttp3IdlingResource -import org.fnives.test.showcase.testutils.safeClose +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt index 7ffc9dd..4e4369b 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt @@ -7,7 +7,7 @@ import androidx.test.runner.intent.IntentStubberRegistry import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.testutils.safeClose +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.ui.auth.AuthActivity import org.fnives.test.showcase.ui.home.HomeRobot import org.fnives.test.showcase.ui.home.MainActivity diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt index bc412e8..f1434d0 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt @@ -12,7 +12,7 @@ import org.fnives.test.showcase.testutils.idling.AsyncDiffUtilInstantTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule import org.fnives.test.showcase.testutils.idling.loopMainThreadFor import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources -import org.fnives.test.showcase.testutils.safeClose +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.junit.After import org.junit.Before diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt index a54e6f1..fa03d45 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt @@ -7,7 +7,7 @@ import org.fnives.test.showcase.R import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.testutils.safeClose +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After import org.junit.Before diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt index cb12061..34317da 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt @@ -6,7 +6,7 @@ import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.testutils.safeClose +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout import org.junit.After diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index a75af32..ddf578c 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -21,7 +21,7 @@ project.ext { okhttp_version = "4.9.3" moshi_version = "1.13.0" - testing_androidx_code_version = "1.4.0" + androidx_test_version = "1.4.0" testing_androidx_junit_version = "1.1.3" arch_core_version = "2.1.0" testing_livedata_version = "1.2.0" diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 83ea82d..2a8a148 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -31,4 +31,5 @@ android { dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" + implementation "androidx.test:core:$androidx_test_version" } \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/activity/ActivityScenarioExtensions.kt similarity index 61% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/activity/ActivityScenarioExtensions.kt index 1bf914c..3016b96 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/ActivityScenarioExtensions.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/activity/ActivityScenarioExtensions.kt @@ -1,19 +1,24 @@ -package org.fnives.test.showcase.testutils +package org.fnives.test.showcase.android.testutil.activity import android.app.Activity import androidx.test.core.app.ActivityScenario +/** + * Workaround for issue: https://github.com/android/android-test/issues/676. + * + * Call this instead of ActivityScenario.close(). + */ fun ActivityScenario.safeClose() { workaroundForActivityScenarioCLoseLockingUp() close() } /** - * This should not be needed, we shouldn't use sleep ever. + * This should not be needed, we shouldn't use sleep basically ever. * However, it seems to be and issue described here: https://github.com/android/android-test/issues/676 * * If an activity is finished in code, the ActivityScenario.close() can hang 30 to 45 seconds. - * This sleeps let's the Activity finish it state change and unlocks the ActivityScenario. + * This sleep let's the Activity finish it's state change and unlocks the ActivityScenario. * * As soon as that issue is closed, this should be removed as well. */ From bbe077dde8d56e37957dcb71d5fbbd706ada46f3 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 15:41:20 +0300 Subject: [PATCH 06/21] Issue#67 Extract MainThread synchronization into a separate module --- .../endtoend/LoginLogoutEndToEndTest.kt | 2 +- .../testutils/idling/awaitIdlingResources.kt | 23 +------------------ .../ui/home/MainActivityInstrumentedTest.kt | 8 ++----- gradlescripts/versions.gradle | 2 +- test-util-android/build.gradle | 1 + ...Thread.kt => mainThreadSynchronization.kt} | 11 +++++++++ .../testutil/viewaction}/LoopMainThreadFor.kt | 14 ++--------- .../viewaction/LoopMainThreadUntilIdle.kt | 17 ++++++++++++++ 8 files changed, 36 insertions(+), 42 deletions(-) rename test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/{doBlockinglyOnMainThread.kt => mainThreadSynchronization.kt} (63%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction}/LoopMainThreadFor.kt (58%) create mode 100644 test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt diff --git a/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt index 911f845..4cd6c5f 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt @@ -16,7 +16,7 @@ import org.fnives.test.showcase.testutils.idling.CompositeDisposable import org.fnives.test.showcase.testutils.idling.Disposable import org.fnives.test.showcase.testutils.idling.IdlingResourceDisposable import org.fnives.test.showcase.testutils.idling.OkHttp3IdlingResource -import org.fnives.test.showcase.testutils.idling.loopMainThreadFor +import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.fnives.test.showcase.ui.splash.SplashActivity import org.hamcrest.Description diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt index 21996b5..1814f2a 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt @@ -1,12 +1,8 @@ package org.fnives.test.showcase.testutils.idling -import android.os.Looper -import androidx.test.espresso.Espresso import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingResource -import androidx.test.espresso.matcher.ViewMatchers -import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadFor -import org.fnives.test.showcase.testutils.viewactions.LoopMainThreadUntilIdle +import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor import java.util.concurrent.Executors // workaround, issue with idlingResources is tracked here https://github.com/robolectric/robolectric/issues/4807 @@ -41,20 +37,3 @@ private fun IdlingResource.awaitUntilIdle() { Thread.sleep(100L) } } - -fun loopMainThreadUntilIdleWithIdlingResources() { - Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) // advance until a request is sent - while (anyResourceIdling()) { // check if any request is in progress - awaitIdlingResources() // complete all requests and other idling resources - Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) // run coroutines after request is finished - } - Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadUntilIdle()) -} - -fun loopMainThreadFor(delay: Long) { - if (Looper.getMainLooper().isCurrentThread) { - Thread.sleep(200L) - } else { - Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(delay)) - } -} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt index f1434d0..b0eb9c4 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt @@ -3,6 +3,7 @@ package org.fnives.test.showcase.ui.home import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.network.mockserver.ContentData import org.fnives.test.showcase.network.mockserver.scenario.content.ContentScenario @@ -10,9 +11,7 @@ import org.fnives.test.showcase.network.mockserver.scenario.refresh.RefreshToken import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.AsyncDiffUtilInstantTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.testutils.idling.loopMainThreadFor -import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources -import org.fnives.test.showcase.android.testutil.activity.safeClose +import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.junit.After import org.junit.Before @@ -175,9 +174,6 @@ class MainActivityInstrumentedTest : KoinTest { robot.swipeRefresh() mainDispatcherTestRule.advanceUntilIdleWithIdlingResources() - loopMainThreadUntilIdleWithIdlingResources() - mainDispatcherTestRule.advanceTimeBy(1000L) - loopMainThreadFor(1000) robot .assertContainsError() diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index ddf578c..f8beeed 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -30,5 +30,5 @@ project.ext { testing_json_assert_version = "1.5.0" testing_junit4_version = "4.12" testing_robolectric_version = "4.7" - testing_espresso_version = "3.4.0" + espresso_version = "3.4.0" } \ No newline at end of file diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 2a8a148..6459217 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -32,4 +32,5 @@ android { dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" implementation "androidx.test:core:$androidx_test_version" + implementation"androidx.test.espresso:espresso-core:$espresso_version" } \ No newline at end of file diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt similarity index 63% rename from test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt index 5df6bd8..f32febe 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/doBlockinglyOnMainThread.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt @@ -2,8 +2,11 @@ package org.fnives.test.showcase.android.testutil.synchronization import android.os.Handler import android.os.Looper +import androidx.test.espresso.Espresso +import androidx.test.espresso.matcher.ViewMatchers import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking +import org.fnives.test.showcase.android.testutil.viewaction.LoopMainThreadFor /** * Runs the given action on the MainThread and blocks currentThread, until it is completed. @@ -22,3 +25,11 @@ fun runOnUIAwaitOnCurrent(action: () -> Unit) { runBlocking { deferred.await() } } } + +fun loopMainThreadFor(delay: Long) { + if (Looper.getMainLooper().thread == Thread.currentThread()) { + Thread.sleep(200L) + } else { + Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(delay)) + } +} \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/LoopMainThreadFor.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt similarity index 58% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/LoopMainThreadFor.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt index 7fe7451..ada5b04 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/LoopMainThreadFor.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.viewactions +package org.fnives.test.showcase.android.testutil.viewaction import android.view.View import androidx.test.espresso.UiController @@ -14,14 +14,4 @@ class LoopMainThreadFor(private val delayInMillis: Long) : ViewAction { override fun perform(uiController: UiController, view: View?) { uiController.loopMainThreadForAtLeast(delayInMillis) } -} - -class LoopMainThreadUntilIdle : ViewAction { - override fun getConstraints(): Matcher = Matchers.isA(View::class.java) - - override fun getDescription(): String = "loop MainThread for until Idle" - - override fun perform(uiController: UiController, view: View?) { - uiController.loopMainThreadUntilIdle() - } -} +} \ No newline at end of file diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt new file mode 100644 index 0000000..99cc28e --- /dev/null +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt @@ -0,0 +1,17 @@ +package org.fnives.test.showcase.android.testutil.viewaction + +import android.view.View +import androidx.test.espresso.UiController +import androidx.test.espresso.ViewAction +import org.hamcrest.Matcher +import org.hamcrest.Matchers + +class LoopMainThreadUntilIdle : ViewAction { + override fun getConstraints(): Matcher = Matchers.isA(View::class.java) + + override fun getDescription(): String = "loop MainThread for until Idle" + + override fun perform(uiController: UiController, view: View?) { + uiController.loopMainThreadUntilIdle() + } +} \ No newline at end of file From 756c74e174b44889bd7bc40ac15ee9ee0fd2246a Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 16:38:14 +0300 Subject: [PATCH 07/21] Issue#67 Extract OkHttp IdlingResources into a separate module --- .../test/showcase/endtoend/LoginLogoutEndToEndTest.kt | 10 +++++----- .../ui/RobolectricAuthActivityInstrumentedTest.kt | 10 +++++----- .../test/showcase/testutils/idling/Disposable.kt | 6 ------ .../testutils/idling/NetworkSynchronizationTestRule.kt | 4 ++++ test-util-android/build.gradle | 1 + .../idlingresources}/CompositeDisposable.kt | 2 +- .../synchronization/idlingresources/Disposable.kt | 6 ++++++ .../idlingresources}/IdlingResourceDisposable.kt | 4 ++-- .../idlingresources}/OkHttp3IdlingResource.kt | 4 ++-- 9 files changed, 26 insertions(+), 21 deletions(-) delete mode 100644 app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/Disposable.kt rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources}/CompositeDisposable.kt (85%) create mode 100644 test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/Disposable.kt rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources}/IdlingResourceDisposable.kt (69%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources}/OkHttp3IdlingResource.kt (94%) diff --git a/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt index 4cd6c5f..9651ea5 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/endtoend/LoginLogoutEndToEndTest.kt @@ -11,12 +11,12 @@ import androidx.test.rule.ActivityTestRule import androidx.test.runner.AndroidJUnit4 import kotlinx.coroutines.test.UnconfinedTestDispatcher import org.fnives.test.showcase.R -import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper -import org.fnives.test.showcase.testutils.idling.CompositeDisposable -import org.fnives.test.showcase.testutils.idling.Disposable -import org.fnives.test.showcase.testutils.idling.IdlingResourceDisposable -import org.fnives.test.showcase.testutils.idling.OkHttp3IdlingResource +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.CompositeDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.Disposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.IdlingResourceDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.OkHttp3IdlingResource import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor +import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.fnives.test.showcase.ui.splash.SplashActivity import org.hamcrest.Description diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt index f66736f..1cc7276 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt @@ -11,15 +11,15 @@ import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain import org.fnives.test.showcase.R +import org.fnives.test.showcase.android.testutil.activity.safeClose +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.CompositeDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.Disposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.IdlingResourceDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.OkHttp3IdlingResource import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper -import org.fnives.test.showcase.testutils.idling.CompositeDisposable -import org.fnives.test.showcase.testutils.idling.Disposable -import org.fnives.test.showcase.testutils.idling.IdlingResourceDisposable import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule.Companion.advanceUntilIdleWithIdlingResources -import org.fnives.test.showcase.testutils.idling.OkHttp3IdlingResource -import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/Disposable.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/Disposable.kt deleted file mode 100644 index 56a3e04..0000000 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/Disposable.kt +++ /dev/null @@ -1,6 +0,0 @@ -package org.fnives.test.showcase.testutils.idling - -interface Disposable { - val isDisposed: Boolean - fun dispose() -} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/NetworkSynchronizationTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/NetworkSynchronizationTestRule.kt index d2cf705..ee3cff5 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/NetworkSynchronizationTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/NetworkSynchronizationTestRule.kt @@ -3,6 +3,10 @@ package org.fnives.test.showcase.testutils.idling import androidx.annotation.CheckResult import androidx.test.espresso.IdlingResource import okhttp3.OkHttpClient +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.CompositeDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.Disposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.IdlingResourceDisposable +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.OkHttp3IdlingResource import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper import org.junit.rules.TestRule import org.junit.runner.Description diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 6459217..91dbe0b 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -33,4 +33,5 @@ dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" implementation "androidx.test:core:$androidx_test_version" implementation"androidx.test.espresso:espresso-core:$espresso_version" + implementation "com.squareup.okhttp3:okhttp:$okhttp_version" } \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/CompositeDisposable.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/CompositeDisposable.kt similarity index 85% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/CompositeDisposable.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/CompositeDisposable.kt index a3fbda4..ff3c139 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/CompositeDisposable.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/CompositeDisposable.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.idling +package org.fnives.test.showcase.android.testutil.synchronization.idlingresources class CompositeDisposable(disposable: List = emptyList()) : Disposable { diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/Disposable.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/Disposable.kt new file mode 100644 index 0000000..32f97a1 --- /dev/null +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/Disposable.kt @@ -0,0 +1,6 @@ +package org.fnives.test.showcase.android.testutil.synchronization.idlingresources + +interface Disposable { + val isDisposed: Boolean + fun dispose() +} diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/IdlingResourceDisposable.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourceDisposable.kt similarity index 69% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/IdlingResourceDisposable.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourceDisposable.kt index df5e2f3..2d2e934 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/IdlingResourceDisposable.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourceDisposable.kt @@ -1,9 +1,9 @@ -package org.fnives.test.showcase.testutils.idling +package org.fnives.test.showcase.android.testutil.synchronization.idlingresources import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingResource -internal class IdlingResourceDisposable(private val idlingResource: IdlingResource) : Disposable { +class IdlingResourceDisposable(private val idlingResource: IdlingResource) : Disposable { override var isDisposed: Boolean = false private set diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/OkHttp3IdlingResource.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/OkHttp3IdlingResource.kt similarity index 94% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/OkHttp3IdlingResource.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/OkHttp3IdlingResource.kt index 14dd5a3..f0770ff 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/OkHttp3IdlingResource.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/OkHttp3IdlingResource.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.idling +package org.fnives.test.showcase.android.testutil.synchronization.idlingresources import androidx.annotation.CheckResult import androidx.annotation.NonNull @@ -40,7 +40,7 @@ class OkHttp3IdlingResource private constructor( * this instance using `Espresso.registerIdlingResources`. */ @CheckResult - @NonNull // Extra guards as a library. + @NonNull fun create(@NonNull name: String?, @NonNull client: OkHttpClient?): OkHttp3IdlingResource { if (name == null) throw NullPointerException("name == null") if (client == null) throw NullPointerException("client == null") From 2c01fbba204e852197895e6df44c8eb08d97f5f5 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 16:45:01 +0300 Subject: [PATCH 08/21] Issue#67 Extract IdlingResources helpers into a separate module --- .../org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt | 2 +- .../fnives/test/showcase/testutils/idling/DispatcherTestRule.kt | 2 ++ .../test/showcase/testutils/idling/MainDispatcherTestRule.kt | 2 ++ .../synchronization/idlingresources/IdlingResourcesHelper.kt | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) rename app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourcesHelper.kt (94%) diff --git a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt index 4ee0571..ad48717 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt @@ -10,7 +10,7 @@ import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.DispatcherTestRule -import org.fnives.test.showcase.testutils.idling.anyResourceIdling +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt index 26a7d43..687a8ce 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt @@ -3,11 +3,13 @@ package org.fnives.test.showcase.testutils.idling import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources @OptIn(ExperimentalCoroutinesApi::class) class DispatcherTestRule : TestRule { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt index 59a1fb9..5721e0e 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt @@ -6,11 +6,13 @@ import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources @OptIn(ExperimentalCoroutinesApi::class) class MainDispatcherTestRule : TestRule { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourcesHelper.kt similarity index 94% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourcesHelper.kt index 1814f2a..e8288f7 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/awaitIdlingResources.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/idlingresources/IdlingResourcesHelper.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.idling +package org.fnives.test.showcase.android.testutil.synchronization.idlingresources import androidx.test.espresso.IdlingRegistry import androidx.test.espresso.IdlingResource From a27f19302a5c5d7447587d740e1bb48b284cb952 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 16:51:33 +0300 Subject: [PATCH 09/21] Issue#67 Extract MainDispatcherTestRule into Library --- .../ui/AuthComposeInstrumentedTest.kt | 4 +- ...RobolectricAuthActivityInstrumentedTest.kt | 2 +- ...tRule.kt => DatabaseDispatcherTestRule.kt} | 4 +- .../idling/MainDispatcherTestRule.kt | 54 ++-------------- .../synchronization/MainDispatcherTestRule.kt | 64 +++++++++++++++++++ 5 files changed, 73 insertions(+), 55 deletions(-) rename app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/{DispatcherTestRule.kt => DatabaseDispatcherTestRule.kt} (97%) create mode 100644 test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/MainDispatcherTestRule.kt diff --git a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt index ad48717..4e3eee0 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt @@ -9,7 +9,7 @@ import org.fnives.test.showcase.core.integration.fake.FakeUserDataLocalStorage import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule -import org.fnives.test.showcase.testutils.idling.DispatcherTestRule +import org.fnives.test.showcase.testutils.idling.DatabaseDispatcherTestRule import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.junit.Before import org.junit.Rule @@ -27,7 +27,7 @@ class AuthComposeInstrumentedTest : KoinTest { private val mockServerScenarioSetupTestRule = MockServerScenarioSetupResetingTestRule() private val mockServerScenarioSetup get() = mockServerScenarioSetupTestRule.mockServerScenarioSetup - private val dispatcherTestRule = DispatcherTestRule() + private val dispatcherTestRule = DatabaseDispatcherTestRule() private lateinit var robot: ComposeLoginRobot private lateinit var navigationRobot: ComposeNavigationRobot diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt index 1cc7276..c665d5a 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricAuthActivityInstrumentedTest.kt @@ -19,7 +19,7 @@ import org.fnives.test.showcase.android.testutil.synchronization.idlingresources import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.network.testutil.NetworkTestConfigurationHelper -import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule.Companion.advanceUntilIdleWithIdlingResources +import org.fnives.test.showcase.testutils.idling.DatabaseDispatcherTestRule.Companion.advanceUntilIdleWithIdlingResources import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DatabaseDispatcherTestRule.kt similarity index 97% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt rename to app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DatabaseDispatcherTestRule.kt index 687a8ce..4708d8b 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/DatabaseDispatcherTestRule.kt @@ -4,15 +4,15 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement -import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources @OptIn(ExperimentalCoroutinesApi::class) -class DispatcherTestRule : TestRule { +class DatabaseDispatcherTestRule : TestRule { private lateinit var testDispatcher: TestDispatcher diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt index 5721e0e..5c1319d 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/idling/MainDispatcherTestRule.kt @@ -1,60 +1,14 @@ package org.fnives.test.showcase.testutils.idling -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling -import org.fnives.test.showcase.android.testutil.synchronization.runOnUIAwaitOnCurrent import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources +import org.fnives.test.showcase.android.testutil.synchronization.MainDispatcherTestRule as LibMainDispatcherTestRule @OptIn(ExperimentalCoroutinesApi::class) -class MainDispatcherTestRule : TestRule { +class MainDispatcherTestRule(useStandard: Boolean = true) : LibMainDispatcherTestRule(useStandard) { - private lateinit var testDispatcher: TestDispatcher - - override fun apply(base: Statement, description: Description): Statement = - object : Statement() { - @Throws(Throwable::class) - override fun evaluate() { - val dispatcher = StandardTestDispatcher() - Dispatchers.setMain(dispatcher) - testDispatcher = dispatcher - TestDatabaseInitialization.overwriteDatabaseInitialization(dispatcher) - try { - base.evaluate() - } finally { - Dispatchers.resetMain() - } - } - } - - fun advanceUntilIdleWithIdlingResources() = runOnUIAwaitOnCurrent { - testDispatcher.advanceUntilIdleWithIdlingResources() - } - - fun advanceUntilIdle() = runOnUIAwaitOnCurrent { - testDispatcher.scheduler.advanceUntilIdle() - } - - fun advanceTimeBy(delayInMillis: Long) = runOnUIAwaitOnCurrent { - testDispatcher.scheduler.advanceTimeBy(delayInMillis) - } - - companion object { - fun TestDispatcher.advanceUntilIdleWithIdlingResources() { - scheduler.advanceUntilIdle() // advance until a request is sent - while (anyResourceIdling()) { // check if any request is in progress - awaitIdlingResources() // complete all requests and other idling resources - scheduler.advanceUntilIdle() // run coroutines after request is finished - } - scheduler.advanceUntilIdle() - } + override fun onTestDispatcherInitialized(testDispatcher: TestDispatcher) { + TestDatabaseInitialization.overwriteDatabaseInitialization(testDispatcher) } } diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/MainDispatcherTestRule.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/MainDispatcherTestRule.kt new file mode 100644 index 0000000..8e2676d --- /dev/null +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/MainDispatcherTestRule.kt @@ -0,0 +1,64 @@ +package org.fnives.test.showcase.android.testutil.synchronization + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.awaitIdlingResources +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +@OptIn(ExperimentalCoroutinesApi::class) +open class MainDispatcherTestRule(private val useStandard: Boolean = true) : TestRule { + + private lateinit var testDispatcher: TestDispatcher + + override fun apply(base: Statement, description: Description): Statement = + object : Statement() { + @Throws(Throwable::class) + override fun evaluate() { + val dispatcher = if (useStandard) StandardTestDispatcher() else UnconfinedTestDispatcher() + Dispatchers.setMain(dispatcher) + testDispatcher = dispatcher + onTestDispatcherInitialized(testDispatcher) + try { + base.evaluate() + } finally { + Dispatchers.resetMain() + onTestDispatcherReset() + } + } + } + + open fun onTestDispatcherInitialized(testDispatcher: TestDispatcher) = Unit + + open fun onTestDispatcherReset() = Unit + + fun advanceUntilIdleWithIdlingResources() = runOnUIAwaitOnCurrent { + testDispatcher.advanceUntilIdleWithIdlingResources() + } + + fun advanceUntilIdle() = runOnUIAwaitOnCurrent { + testDispatcher.scheduler.advanceUntilIdle() + } + + fun advanceTimeBy(delayInMillis: Long) = runOnUIAwaitOnCurrent { + testDispatcher.scheduler.advanceTimeBy(delayInMillis) + } + + companion object { + fun TestDispatcher.advanceUntilIdleWithIdlingResources() { + scheduler.advanceUntilIdle() // advance until a request is sent + while (anyResourceIdling()) { // check if any request is in progress + awaitIdlingResources() // complete all requests and other idling resources + scheduler.advanceUntilIdle() // run coroutines after request is finished + } + scheduler.advanceUntilIdle() + } + } +} From 99141c0f17ae84f401b361c41ad8189978cdcd86 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 27 May 2022 17:55:38 +0300 Subject: [PATCH 10/21] Issue#67 Extract ViewActions into Library --- .../test/showcase/ui/RobolectricLoginRobot.kt | 13 ++++++------- .../fnives/test/showcase/ui/home/HomeRobot.kt | 6 +++--- .../test/showcase/ui/login/LoginRobot.kt | 15 +++++++-------- .../login/codekata/CodeKataSharedRobotTest.kt | 13 ++++++------- .../test/showcase/ui/splash/SplashRobot.kt | 2 +- test-util-android/build.gradle | 9 ++++++++- .../android/testutil/intent}/notIntended.kt | 2 +- .../snackbar}/SnackbarVerificationHelper.kt | 18 ++++++++++-------- .../viewaction/imageview}/WithDrawable.kt | 4 ++-- .../ReplaceProgressBarDrawableToStatic.kt | 2 +- .../viewaction/swiperefresh}/PullToRefresh.kt | 2 +- .../SwipeRefreshLayoutExtension.kt | 0 12 files changed, 46 insertions(+), 40 deletions(-) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent}/notIntended.kt (89%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar}/SnackbarVerificationHelper.kt (70%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/imageview}/WithDrawable.kt (89%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/progressbar}/ReplaceProgressBarDrawableToStatic.kt (91%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh}/PullToRefresh.kt (95%) rename {app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions => test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh}/SwipeRefreshLayoutExtension.kt (100%) diff --git a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricLoginRobot.kt b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricLoginRobot.kt index 8ed9cf6..8a912e6 100644 --- a/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricLoginRobot.kt +++ b/app/src/robolectricTest/java/org/fnives/test/showcase/ui/RobolectricLoginRobot.kt @@ -10,14 +10,13 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import org.fnives.test.showcase.R -import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper -import org.fnives.test.showcase.testutils.viewactions.notIntended +import org.fnives.test.showcase.android.testutil.intent.notIntended +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText import org.fnives.test.showcase.ui.home.MainActivity import org.hamcrest.core.IsNot.not -class RobolectricLoginRobot( - private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper() -) { +class RobolectricLoginRobot { fun setUsername(username: String): RobolectricLoginRobot = apply { onView(withId(R.id.user_edit_text)) @@ -55,11 +54,11 @@ class RobolectricLoginRobot( } fun assertErrorIsShown(@StringRes stringResID: Int) = apply { - snackbarVerificationHelper.assertIsShownWithText(stringResID) + assertSnackBarIsShownWithText(stringResID) } fun assertErrorIsNotShown() = apply { - snackbarVerificationHelper.assertIsNotShown() + assertSnackBarIsNotShown() } fun assertNavigatedToHome() = apply { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/HomeRobot.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/HomeRobot.kt index fe95319..7b154a6 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/HomeRobot.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/HomeRobot.kt @@ -19,11 +19,11 @@ import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withParent import androidx.test.espresso.matcher.ViewMatchers.withText import org.fnives.test.showcase.R +import org.fnives.test.showcase.android.testutil.intent.notIntended +import org.fnives.test.showcase.android.testutil.viewaction.imageview.WithDrawable +import org.fnives.test.showcase.android.testutil.viewaction.swiperefresh.PullToRefresh import org.fnives.test.showcase.model.content.Content import org.fnives.test.showcase.model.content.FavouriteContent -import org.fnives.test.showcase.testutils.viewactions.PullToRefresh -import org.fnives.test.showcase.testutils.viewactions.WithDrawable -import org.fnives.test.showcase.testutils.viewactions.notIntended import org.fnives.test.showcase.ui.auth.AuthActivity import org.hamcrest.Matchers.allOf diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt index 0419eec..6ec7cd5 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/LoginRobot.kt @@ -14,15 +14,14 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId import org.fnives.test.showcase.R -import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper -import org.fnives.test.showcase.testutils.viewactions.ReplaceProgressBarDrawableToStatic -import org.fnives.test.showcase.testutils.viewactions.notIntended +import org.fnives.test.showcase.android.testutil.intent.notIntended +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText +import org.fnives.test.showcase.android.testutil.viewaction.progressbar.ReplaceProgressBarDrawableToStatic import org.fnives.test.showcase.ui.home.MainActivity import org.hamcrest.core.IsNot.not -class LoginRobot( - private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper() -) { +class LoginRobot { fun setupIntentResults() { Intents.intending(hasComponent(MainActivity::class.java.canonicalName)) @@ -68,7 +67,7 @@ class LoginRobot( } fun assertErrorIsShown(@StringRes stringResID: Int) = apply { - snackbarVerificationHelper.assertIsShownWithText(stringResID) + assertSnackBarIsShownWithText(stringResID) } fun assertLoadingBeforeRequests() = apply { @@ -82,7 +81,7 @@ class LoginRobot( } fun assertErrorIsNotShown() = apply { - snackbarVerificationHelper.assertIsNotShown() + assertSnackBarIsNotShown() } fun assertNavigatedToHome() = apply { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt index fb87018..2c1b5bc 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt @@ -8,14 +8,13 @@ import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.ViewMatchers import org.fnives.test.showcase.R -import org.fnives.test.showcase.testutils.configuration.SnackbarVerificationHelper -import org.fnives.test.showcase.testutils.viewactions.notIntended +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown +import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText +import org.fnives.test.showcase.android.testutil.intent.notIntended import org.fnives.test.showcase.ui.home.MainActivity import org.hamcrest.core.IsNot -class CodeKataSharedRobotTest( - private val snackbarVerificationHelper: SnackbarVerificationHelper = SnackbarVerificationHelper() -) { +class CodeKataSharedRobotTest { fun setUsername(username: String): CodeKataSharedRobotTest = apply { Espresso.onView(ViewMatchers.withId(R.id.user_edit_text)) @@ -53,11 +52,11 @@ class CodeKataSharedRobotTest( } fun assertErrorIsShown(@StringRes stringResID: Int): CodeKataSharedRobotTest = apply { - snackbarVerificationHelper.assertIsShownWithText(stringResID) + assertSnackBarIsShownWithText(stringResID) } fun assertErrorIsNotShown(): CodeKataSharedRobotTest = apply { - snackbarVerificationHelper.assertIsNotShown() + assertSnackBarIsNotShown() } fun assertNavigatedToHome(): CodeKataSharedRobotTest = apply { diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashRobot.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashRobot.kt index 0e941b6..c598d77 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashRobot.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashRobot.kt @@ -5,7 +5,7 @@ import android.app.Instrumentation import android.content.Intent import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers -import org.fnives.test.showcase.testutils.viewactions.notIntended +import org.fnives.test.showcase.android.testutil.intent.notIntended import org.fnives.test.showcase.ui.auth.AuthActivity import org.fnives.test.showcase.ui.home.MainActivity diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 91dbe0b..6530663 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -31,7 +31,14 @@ android { dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" + implementation "androidx.test:core:$androidx_test_version" - implementation"androidx.test.espresso:espresso-core:$espresso_version" + implementation "androidx.test.espresso:espresso-core:$espresso_version" + implementation "androidx.test.espresso:espresso-intents:$espresso_version" + implementation "com.squareup.okhttp3:okhttp:$okhttp_version" + + implementation "com.google.android.material:material:$androidx_material_version" + implementation "androidx.swiperefreshlayout:swiperefreshlayout:$androidx_swiperefreshlayout_version" + implementation "androidx.core:core-ktx:$androidx_core_version" } \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/notIntended.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt similarity index 89% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/notIntended.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt index a88cf48..d520eb3 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/notIntended.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.viewactions +package org.fnives.test.showcase.android.testutil.intent import android.content.Intent import androidx.test.espresso.intent.Intents.intended diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt similarity index 70% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt index 3827bd7..3d49d99 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/configuration/SnackbarVerificationHelper.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt @@ -1,5 +1,6 @@ -package org.fnives.test.showcase.testutils.configuration +package org.fnives.test.showcase.android.testutil.snackbar +import android.annotation.SuppressLint import android.view.View import androidx.annotation.StringRes import androidx.test.espresso.Espresso @@ -8,15 +9,16 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers -import com.google.android.material.R +import com.google.android.material.R as MaterialR import com.google.android.material.snackbar.Snackbar import org.hamcrest.Matcher import org.hamcrest.Matchers -class SnackbarVerificationHelper { +object SnackbarVerificationHelper { - fun assertIsShownWithText(@StringRes stringResID: Int, doDismiss: Boolean = true) { - Espresso.onView(ViewMatchers.withId(R.id.snackbar_text)) + @SuppressLint("RestrictedApi") + fun assertSnackBarIsShownWithText(@StringRes stringResID: Int, doDismiss: Boolean = true) { + Espresso.onView(ViewMatchers.withId(MaterialR.id.snackbar_text)) .check(ViewAssertions.matches(ViewMatchers.withText(stringResID))) if (doDismiss) { Espresso.onView(ViewMatchers.isAssignableFrom(Snackbar.SnackbarLayout::class.java)).perform(ViewActions.swipeRight()) @@ -24,8 +26,8 @@ class SnackbarVerificationHelper { } } - fun assertIsNotShown() { - Espresso.onView(ViewMatchers.withId(R.id.snackbar_text)).check(ViewAssertions.doesNotExist()) + fun assertSnackBarIsNotShown() { + Espresso.onView(ViewMatchers.withId(MaterialR.id.snackbar_text)).check(ViewAssertions.doesNotExist()) } class LoopMainUntilSnackbarDismissed : ViewAction { @@ -39,4 +41,4 @@ class SnackbarVerificationHelper { } } } -} +} \ No newline at end of file diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/WithDrawable.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/imageview/WithDrawable.kt similarity index 89% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/WithDrawable.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/imageview/WithDrawable.kt index 79287c1..2303224 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/WithDrawable.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/imageview/WithDrawable.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.viewactions +package org.fnives.test.showcase.android.testutil.viewaction.imageview import android.content.res.ColorStateList import android.graphics.PorterDuff @@ -26,7 +26,7 @@ class WithDrawable( override fun matchesSafely(view: View): Boolean { val context = view.context val tintColor = tint?.let { ContextCompat.getColor(view.context, it) } - val expectedBitmap = context.getDrawable(id)?.apply { + val expectedBitmap = ContextCompat.getDrawable(context, id)?.apply { if (tintColor != null) { setTintList(ColorStateList.valueOf(tintColor)) setTintMode(tintMode) diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/progressbar/ReplaceProgressBarDrawableToStatic.kt similarity index 91% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/progressbar/ReplaceProgressBarDrawableToStatic.kt index 44a6367..8293afa 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/ReplaceProgressBarDrawableToStatic.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/progressbar/ReplaceProgressBarDrawableToStatic.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.viewactions +package org.fnives.test.showcase.android.testutil.viewaction.progressbar import android.graphics.Color import android.graphics.drawable.ColorDrawable diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh/PullToRefresh.kt similarity index 95% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh/PullToRefresh.kt index d1b4751..73cecda 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/PullToRefresh.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh/PullToRefresh.kt @@ -1,4 +1,4 @@ -package org.fnives.test.showcase.testutils.viewactions +package org.fnives.test.showcase.android.testutil.viewaction.swiperefresh import android.view.View import androidx.swiperefreshlayout.widget.SwipeRefreshLayout diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/SwipeRefreshLayoutExtension.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh/SwipeRefreshLayoutExtension.kt similarity index 100% rename from app/src/sharedTest/java/org/fnives/test/showcase/testutils/viewactions/SwipeRefreshLayoutExtension.kt rename to test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/swiperefresh/SwipeRefreshLayoutExtension.kt From 8d60a37b1618cdf7b5fc1c444cefa01de591212e Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 31 May 2022 12:31:28 +0300 Subject: [PATCH 11/21] Issue#67 Fix Code Analysis errors --- .../showcase/ui/AuthComposeInstrumentedTest.kt | 2 +- .../statesetup/SetupAuthenticationState.kt | 6 +++--- .../ui/home/MainActivityInstrumentedTest.kt | 2 +- .../ui/login/AuthActivityInstrumentedTest.kt | 2 +- .../login/codekata/CodeKataSharedRobotTest.kt | 2 +- .../ui/splash/SplashActivityInstrumentedTest.kt | 2 +- .../test/showcase/ui/auth/AuthViewModelTest.kt | 5 +++++ gradlescripts/testdependencies.gradle | 2 +- gradlescripts/versions.gradle | 2 +- .../android/testutil/intent/notIntended.kt | 1 + .../snackbar/SnackbarVerificationHelper.kt | 4 ++-- .../mainThreadSynchronization.kt | 2 +- .../testutil/viewaction/LoopMainThreadFor.kt | 2 +- .../viewaction/LoopMainThreadUntilIdle.kt | 2 +- .../android/testutil/SharedMigrationTestRule.kt | 2 +- .../testutil/SharedMigrationTestRuleFactory.kt | 17 +++++++++-------- 16 files changed, 31 insertions(+), 24 deletions(-) diff --git a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt index 4e3eee0..a402297 100644 --- a/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt +++ b/app/src/androidTest/java/org/fnives/test/showcase/ui/AuthComposeInstrumentedTest.kt @@ -4,13 +4,13 @@ import androidx.compose.ui.test.junit4.StateRestorationTester import androidx.compose.ui.test.junit4.createComposeRule import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.R +import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.fnives.test.showcase.compose.screen.AppNavigation import org.fnives.test.showcase.core.integration.fake.FakeUserDataLocalStorage import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.DatabaseDispatcherTestRule -import org.fnives.test.showcase.android.testutil.synchronization.idlingresources.anyResourceIdling import org.junit.Before import org.junit.Rule import org.junit.Test diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt index 4e4369b..411f6ea 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/testutils/statesetup/SetupAuthenticationState.kt @@ -4,10 +4,10 @@ import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.runner.intent.IntentStubberRegistry +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.network.mockserver.MockServerScenarioSetup import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.ui.auth.AuthActivity import org.fnives.test.showcase.ui.home.HomeRobot import org.fnives.test.showcase.ui.home.MainActivity @@ -19,7 +19,7 @@ object SetupAuthenticationState : KoinTest { fun setupLogin( mainDispatcherTestRule: MainDispatcherTestRule, mockServerScenarioSetup: MockServerScenarioSetup, - resetIntents: Boolean = true + resetIntents: Boolean = true, ) { resetIntentsIfNeeded(resetIntents) { mockServerScenarioSetup.setScenario(AuthScenario.Success(username = "a", password = "b")) @@ -40,7 +40,7 @@ object SetupAuthenticationState : KoinTest { fun setupLogout( mainDispatcherTestRule: MainDispatcherTestRule, - resetIntents: Boolean = true + resetIntents: Boolean = true, ) { resetIntentsIfNeeded(resetIntents) { val activityScenario = ActivityScenario.launch(MainActivity::class.java) diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt index b0eb9c4..1517396 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/home/MainActivityInstrumentedTest.kt @@ -4,6 +4,7 @@ import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.android.testutil.activity.safeClose +import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.network.mockserver.ContentData import org.fnives.test.showcase.network.mockserver.scenario.content.ContentScenario @@ -11,7 +12,6 @@ import org.fnives.test.showcase.network.mockserver.scenario.refresh.RefreshToken import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.AsyncDiffUtilInstantTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.android.testutil.synchronization.loopMainThreadFor import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.junit.After import org.junit.Before diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt index fa03d45..7b31b8c 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/AuthActivityInstrumentedTest.kt @@ -4,10 +4,10 @@ import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 import org.fnives.test.showcase.R +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.network.mockserver.scenario.auth.AuthScenario import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.ui.auth.AuthActivity import org.junit.After import org.junit.Before diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt index 2c1b5bc..2123cab 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/login/codekata/CodeKataSharedRobotTest.kt @@ -8,9 +8,9 @@ import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers import androidx.test.espresso.matcher.ViewMatchers import org.fnives.test.showcase.R +import org.fnives.test.showcase.android.testutil.intent.notIntended import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText -import org.fnives.test.showcase.android.testutil.intent.notIntended import org.fnives.test.showcase.ui.home.MainActivity import org.hamcrest.core.IsNot diff --git a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt index 34317da..94e34d8 100644 --- a/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt +++ b/app/src/sharedTest/java/org/fnives/test/showcase/ui/splash/SplashActivityInstrumentedTest.kt @@ -4,9 +4,9 @@ import androidx.lifecycle.Lifecycle import androidx.test.core.app.ActivityScenario import androidx.test.espresso.intent.Intents import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.MockServerScenarioSetupResetingTestRule import org.fnives.test.showcase.testutils.idling.MainDispatcherTestRule -import org.fnives.test.showcase.android.testutil.activity.safeClose import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout import org.junit.After diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt index 8d9a4af..64570b3 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt @@ -44,6 +44,11 @@ internal class AuthViewModelTest { @DisplayName("GIVEN initialized viewModel WHEN observed THEN loading false other fields are empty") @Test fun initialSetup() { + var a = "" + repeat(1200 / 2) { + a += "a " + } + System.err.println(a+"b") val usernameTestObserver = sut.username.test() val passwordTestObserver = sut.password.test() val loadingTestObserver = sut.loading.test() diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle index c43b981..05ed31e 100644 --- a/gradlescripts/testdependencies.gradle +++ b/gradlescripts/testdependencies.gradle @@ -49,7 +49,7 @@ project.ext { def testing_json_assert_version = propertyOrNull('json_assert_version') ?: "1.5.0" def testing_okhttp3 = propertyOrNull('okhttp_version') ?: "4.9.3" def testing_turbine_version = propertyOrNull('turbine_version') ?: "0.7.0" - def testing_androidx_room_version = propertyOrNull('room_version') ?: "2.4.1" + def testing_androidx_room_version = propertyOrNull('room_version') ?: "2.4.2" def testing_livedata_version = propertyOrNull('testing_livedata_version') ?: "1.2.0" def testing_compose_version = propertyOrNull('compose_version') ?: "1.1.0" def testing_hamcrest_version = propertyOrNull('hamcrest_version')?: "2.2" diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index f8beeed..031de2a 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -5,7 +5,7 @@ project.ext { androidx_constraintlayout_version = "2.1.3" androidx_livedata_version = "2.4.0" androidx_swiperefreshlayout_version = "1.1.0" - room_version = "2.4.1" + room_version = "2.4.2" activity_ktx_version = "1.4.0" androidx_navigation = "2.4.0" diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt index d520eb3..a9c6123 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/intent/notIntended.kt @@ -5,6 +5,7 @@ import androidx.test.espresso.intent.Intents.intended import org.hamcrest.Matcher import org.hamcrest.StringDescription +@Suppress("SwallowedException") fun notIntended(matcher: Matcher) { try { intended(matcher) diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt index 3d49d99..6065d14 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/snackbar/SnackbarVerificationHelper.kt @@ -9,10 +9,10 @@ import androidx.test.espresso.ViewAction import androidx.test.espresso.action.ViewActions import androidx.test.espresso.assertion.ViewAssertions import androidx.test.espresso.matcher.ViewMatchers -import com.google.android.material.R as MaterialR import com.google.android.material.snackbar.Snackbar import org.hamcrest.Matcher import org.hamcrest.Matchers +import com.google.android.material.R as MaterialR object SnackbarVerificationHelper { @@ -41,4 +41,4 @@ object SnackbarVerificationHelper { } } } -} \ No newline at end of file +} diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt index f32febe..25a3518 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/synchronization/mainThreadSynchronization.kt @@ -32,4 +32,4 @@ fun loopMainThreadFor(delay: Long) { } else { Espresso.onView(ViewMatchers.isRoot()).perform(LoopMainThreadFor(delay)) } -} \ No newline at end of file +} diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt index ada5b04..a298b8c 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadFor.kt @@ -14,4 +14,4 @@ class LoopMainThreadFor(private val delayInMillis: Long) : ViewAction { override fun perform(uiController: UiController, view: View?) { uiController.loopMainThreadForAtLeast(delayInMillis) } -} \ No newline at end of file +} diff --git a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt index 99cc28e..81fe989 100644 --- a/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt +++ b/test-util-android/src/main/java/org/fnives/test/showcase/android/testutil/viewaction/LoopMainThreadUntilIdle.kt @@ -14,4 +14,4 @@ class LoopMainThreadUntilIdle : ViewAction { override fun perform(uiController: UiController, view: View?) { uiController.loopMainThreadUntilIdle() } -} \ No newline at end of file +} diff --git a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt index b35c199..efee50e 100644 --- a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt +++ b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRule.kt @@ -26,4 +26,4 @@ interface SharedMigrationTestRule : TestRule { fun closeWhenFinished(db: SupportSQLiteDatabase) fun closeWhenFinished(db: RoomDatabase) -} \ No newline at end of file +} diff --git a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt index 2e3db83..cd49bdb 100644 --- a/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt +++ b/test-util-shared-robolectric/src/main/java/org/fnives/test/showcase/android/testutil/SharedMigrationTestRuleFactory.kt @@ -6,8 +6,8 @@ import androidx.room.migration.AutoMigrationSpec import androidx.sqlite.db.SupportSQLiteOpenHelper import org.fnives.test.showcase.android.testutil.robolectric.RobolectricMigrationTestRule -inline fun SharedMigrationTestRule( - instrumentation: Instrumentation +inline fun SharedMigrationTestRule( + instrumentation: Instrumentation, ): SharedMigrationTestRule = createAndroidClassOrRobolectric( androidClassFactory = { androidClass -> @@ -22,9 +22,9 @@ inline fun SharedMigrationTestRule( } ) -inline fun SharedMigrationTestRule( +inline fun SharedMigrationTestRule( instrumentation: Instrumentation, - specs: List + specs: List, ): SharedMigrationTestRule = createAndroidClassOrRobolectric( androidClassFactory = { androidClass -> @@ -40,10 +40,10 @@ inline fun SharedMigrationTestRule( } ) -inline fun SharedMigrationTestRule( +inline fun SharedMigrationTestRule( instrumentation: Instrumentation, specs: List, - openFactory: SupportSQLiteOpenHelper.Factory + openFactory: SupportSQLiteOpenHelper.Factory, ): SharedMigrationTestRule = createAndroidClassOrRobolectric( androidClassFactory = { androidClass -> @@ -62,7 +62,7 @@ inline fun SharedMigrationTestRule( fun createAndroidClassOrRobolectric( androidClassFactory: (Class<*>) -> Any, - robolectricFactory: () -> SharedMigrationTestRule + robolectricFactory: () -> SharedMigrationTestRule, ): SharedMigrationTestRule { val androidClass = getAndroidClass() return if (androidClass == null) { @@ -72,8 +72,9 @@ fun createAndroidClassOrRobolectric( } } +@Suppress("SwallowedException") private fun getAndroidClass() = try { Class.forName("org.fnives.test.showcase.android.testutil.AndroidMigrationTestRule") } catch (classNotFoundException: ClassNotFoundException) { null -} \ No newline at end of file +} From 3b96a5d9eb893fc92ce8c9203a266e2e017af006 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 11:31:06 +0300 Subject: [PATCH 12/21] Issue#67 Extract InstantExecutorExtension --- app/build.gradle | 5 +-- .../showcase/ui/auth/AuthViewModelTest.kt | 2 +- .../showcase/ui/auth/CodeKataAuthViewModel.kt | 2 +- .../showcase/ui/home/MainViewModelTest.kt | 2 +- .../showcase/ui/splash/SplashViewModelTest.kt | 2 +- gradlescripts/versions.gradle | 2 +- network/build.gradle | 2 +- settings.gradle | 1 + test-util-junit5-android/.gitignore | 1 + test-util-junit5-android/build.gradle | 35 +++++++++++++++++++ test-util-junit5-android/consumer-rules.pro | 0 test-util-junit5-android/proguard-rules.pro | 21 +++++++++++ .../src/main/AndroidManifest.xml | 5 +++ .../testutil}/InstantExecutorExtension.kt | 4 ++- 14 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 test-util-junit5-android/.gitignore create mode 100644 test-util-junit5-android/build.gradle create mode 100644 test-util-junit5-android/consumer-rules.pro create mode 100644 test-util-junit5-android/proguard-rules.pro create mode 100644 test-util-junit5-android/src/main/AndroidManifest.xml rename {app/src/test/java/org/fnives/test/showcase/testutils => test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil}/InstantExecutorExtension.kt (91%) diff --git a/app/build.gradle b/app/build.gradle index 37ae54c..85095b1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -107,10 +107,11 @@ dependencies { androidTestImplementation project(':mockserver') testImplementation testFixtures(project(':core')) + testImplementation project(':test-util-junit5-android') androidTestImplementation testFixtures(project(':core')) - implementation project(':test-util-shared-robolectric') + testImplementation project(':test-util-shared-robolectric') androidTestImplementation project(':test-util-shared-android') - implementation project(':test-util-android') + testImplementation project(':test-util-android') androidTestImplementation project(':test-util-android') } diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt index 64570b3..70767f5 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt @@ -3,11 +3,11 @@ package org.fnives.test.showcase.ui.auth import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking +import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.login.LoginUseCase import org.fnives.test.showcase.model.auth.LoginCredentials import org.fnives.test.showcase.model.auth.LoginStatus import org.fnives.test.showcase.model.shared.Answer -import org.fnives.test.showcase.testutils.InstantExecutorExtension import org.fnives.test.showcase.testutils.TestMainDispatcher import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt index 7a70ed0..4de4386 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt @@ -1,8 +1,8 @@ package org.fnives.test.showcase.ui.auth import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.login.LoginUseCase -import org.fnives.test.showcase.testutils.InstantExecutorExtension import org.fnives.test.showcase.testutils.TestMainDispatcher import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName diff --git a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt index e0ca3e8..6173944 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt @@ -4,6 +4,7 @@ import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.content.AddContentToFavouriteUseCase import org.fnives.test.showcase.core.content.FetchContentUseCase import org.fnives.test.showcase.core.content.GetAllContentUseCase @@ -14,7 +15,6 @@ import org.fnives.test.showcase.model.content.ContentId import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.model.content.ImageUrl import org.fnives.test.showcase.model.shared.Resource -import org.fnives.test.showcase.testutils.InstantExecutorExtension import org.fnives.test.showcase.testutils.TestMainDispatcher import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName diff --git a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt index bd366e1..01d59f1 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt @@ -2,8 +2,8 @@ package org.fnives.test.showcase.ui.splash import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi +import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase -import org.fnives.test.showcase.testutils.InstantExecutorExtension import org.fnives.test.showcase.testutils.TestMainDispatcher import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index 031de2a..5362774 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -26,7 +26,7 @@ project.ext { arch_core_version = "2.1.0" testing_livedata_version = "1.2.0" testing_kotlin_mockito_version = "4.0.0" - testing_junit5_version = "5.7.0" + junit5_version = "5.7.0" testing_json_assert_version = "1.5.0" testing_junit4_version = "4.12" testing_robolectric_version = "4.7" diff --git a/network/build.gradle b/network/build.gradle index ed5e6f7..db2bc59 100644 --- a/network/build.gradle +++ b/network/build.gradle @@ -27,6 +27,6 @@ dependencies { applyNetworkTestDependenciesTo(this) testFixturesApi project(':mockserver') - testFixturesApi "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" + testFixturesApi "org.junit.jupiter:junit-jupiter-engine:$junit5_version" testFixturesApi "io.insert-koin:koin-test-junit5:$koin_version" } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7e8bd01..6b11768 100644 --- a/settings.gradle +++ b/settings.gradle @@ -7,3 +7,4 @@ include ':app' include ':test-util-shared-android' include ':test-util-shared-robolectric' include ':test-util-android' +include ':test-util-junit5-android' diff --git a/test-util-junit5-android/.gitignore b/test-util-junit5-android/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/test-util-junit5-android/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-util-junit5-android/build.gradle b/test-util-junit5-android/build.gradle new file mode 100644 index 0000000..dd13d96 --- /dev/null +++ b/test-util-junit5-android/build.gradle @@ -0,0 +1,35 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 21 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation "org.junit.jupiter:junit-jupiter-engine:$junit5_version" + implementation "androidx.arch.core:core-runtime:$arch_core_version" +} \ No newline at end of file diff --git a/test-util-junit5-android/consumer-rules.pro b/test-util-junit5-android/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/test-util-junit5-android/proguard-rules.pro b/test-util-junit5-android/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/test-util-junit5-android/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/test-util-junit5-android/src/main/AndroidManifest.xml b/test-util-junit5-android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..80e5dbb --- /dev/null +++ b/test-util-junit5-android/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/app/src/test/java/org/fnives/test/showcase/testutils/InstantExecutorExtension.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt similarity index 91% rename from app/src/test/java/org/fnives/test/showcase/testutils/InstantExecutorExtension.kt rename to test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt index ca9d988..a3b109e 100644 --- a/app/src/test/java/org/fnives/test/showcase/testutils/InstantExecutorExtension.kt +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt @@ -1,5 +1,6 @@ -package org.fnives.test.showcase.testutils +package org.fnives.test.showcase.android.testutil +import android.annotation.SuppressLint import androidx.arch.core.executor.ArchTaskExecutor import androidx.arch.core.executor.TaskExecutor import org.junit.jupiter.api.extension.AfterEachCallback @@ -14,6 +15,7 @@ import org.junit.jupiter.api.extension.ExtensionContext * A JUnit5 Extensions that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. * You can use this extension for your host side tests that use Architecture Components. */ +@SuppressLint("RestrictedApi") class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback { override fun beforeEach(context: ExtensionContext?) { From 7e019973e8d0efa16326671d1a94fb6830902265 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 11:47:22 +0300 Subject: [PATCH 13/21] Issue#67 Extract JUnit5 MainDispatcher --- .../org/fnives/test/showcase/di/DITest.kt | 4 +- .../showcase/testutils/TestMainDispatcher.kt | 39 ------------------- .../showcase/ui/auth/AuthViewModelTest.kt | 6 +-- .../showcase/ui/auth/CodeKataAuthViewModel.kt | 6 +-- .../showcase/ui/home/MainViewModelTest.kt | 6 +-- .../showcase/ui/splash/SplashViewModelTest.kt | 6 +-- codekata/viewmodel.instructionset.md | 8 ++-- test-util-junit5-android/build.gradle | 2 + .../testutil/StandardTestMainDispatcher.kt | 30 ++++++++++++++ .../android/testutil/TestMainDispatcher.kt | 32 +++++++++++++++ .../testutil/UnconfinedTestMainDispatcher.kt | 30 ++++++++++++++ 11 files changed, 112 insertions(+), 57 deletions(-) delete mode 100644 app/src/test/java/org/fnives/test/showcase/testutils/TestMainDispatcher.kt create mode 100644 test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/StandardTestMainDispatcher.kt create mode 100644 test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt create mode 100644 test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/UnconfinedTestMainDispatcher.kt diff --git a/app/src/test/java/org/fnives/test/showcase/di/DITest.kt b/app/src/test/java/org/fnives/test/showcase/di/DITest.kt index cf595ba..bc950e5 100644 --- a/app/src/test/java/org/fnives/test/showcase/di/DITest.kt +++ b/app/src/test/java/org/fnives/test/showcase/di/DITest.kt @@ -2,7 +2,7 @@ package org.fnives.test.showcase.di import android.content.Context import org.fnives.test.showcase.model.network.BaseUrl -import org.fnives.test.showcase.testutils.TestMainDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.ui.auth.AuthViewModel import org.fnives.test.showcase.ui.home.MainViewModel import org.fnives.test.showcase.ui.splash.SplashViewModel @@ -20,7 +20,7 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -@ExtendWith(TestMainDispatcher::class) +@ExtendWith(StandardTestMainDispatcher::class) class DITest : KoinTest { private val authViewModel by inject() diff --git a/app/src/test/java/org/fnives/test/showcase/testutils/TestMainDispatcher.kt b/app/src/test/java/org/fnives/test/showcase/testutils/TestMainDispatcher.kt deleted file mode 100644 index 77beff4..0000000 --- a/app/src/test/java/org/fnives/test/showcase/testutils/TestMainDispatcher.kt +++ /dev/null @@ -1,39 +0,0 @@ -package org.fnives.test.showcase.testutils - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.TestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.setMain -import org.fnives.test.showcase.testutils.TestMainDispatcher.Companion.testDispatcher -import org.junit.jupiter.api.extension.AfterEachCallback -import org.junit.jupiter.api.extension.BeforeEachCallback -import org.junit.jupiter.api.extension.ExtensionContext - -/** - * Custom Junit5 Extension which replaces the main dispatcher with a [TestDispatcher] - * - * One can access the test dispatcher via [testDispatcher] static getter. - */ -@OptIn(ExperimentalCoroutinesApi::class) -class TestMainDispatcher : BeforeEachCallback, AfterEachCallback { - - override fun beforeEach(context: ExtensionContext?) { - val testDispatcher = StandardTestDispatcher() - privateTestDispatcher = testDispatcher - Dispatchers.setMain(testDispatcher) - } - - override fun afterEach(context: ExtensionContext?) { - Dispatchers.resetMain() - privateTestDispatcher = null - } - - companion object { - private var privateTestDispatcher: TestDispatcher? = null - val testDispatcher: TestDispatcher - get() = privateTestDispatcher - ?: throw IllegalStateException("TestMainDispatcher is in afterEach State") - } -} diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt index 70767f5..0a1ee75 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt @@ -8,7 +8,7 @@ import org.fnives.test.showcase.core.login.LoginUseCase import org.fnives.test.showcase.model.auth.LoginCredentials import org.fnives.test.showcase.model.auth.LoginStatus import org.fnives.test.showcase.model.shared.Answer -import org.fnives.test.showcase.testutils.TestMainDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -27,13 +27,13 @@ import org.mockito.kotlin.whenever import java.util.stream.Stream @Suppress("TestFunctionName") -@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class) +@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class) @OptIn(ExperimentalCoroutinesApi::class) internal class AuthViewModelTest { private lateinit var sut: AuthViewModel private lateinit var mockLoginUseCase: LoginUseCase - private val testScheduler get() = TestMainDispatcher.testDispatcher.scheduler + private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler @BeforeEach fun setUp() { diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt index 4de4386..94c7055 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt @@ -3,20 +3,20 @@ package org.fnives.test.showcase.ui.auth import kotlinx.coroutines.ExperimentalCoroutinesApi import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.login.LoginUseCase -import org.fnives.test.showcase.testutils.TestMainDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.mockito.kotlin.mock -@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class) +@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class) @OptIn(ExperimentalCoroutinesApi::class) class CodeKataAuthViewModel { private lateinit var sut: AuthViewModel private lateinit var mockLoginUseCase: LoginUseCase - private val testScheduler get() = TestMainDispatcher.testDispatcher.scheduler + private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler @BeforeEach fun setUp() { diff --git a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt index 6173944..8e7402f 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt @@ -15,7 +15,7 @@ import org.fnives.test.showcase.model.content.ContentId import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.model.content.ImageUrl import org.fnives.test.showcase.model.shared.Resource -import org.fnives.test.showcase.testutils.TestMainDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test @@ -29,7 +29,7 @@ import org.mockito.kotlin.verifyNoMoreInteractions import org.mockito.kotlin.whenever @Suppress("TestFunctionName") -@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class) +@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class) @OptIn(ExperimentalCoroutinesApi::class) internal class MainViewModelTest { @@ -39,7 +39,7 @@ internal class MainViewModelTest { private lateinit var mockFetchContentUseCase: FetchContentUseCase private lateinit var mockAddContentToFavouriteUseCase: AddContentToFavouriteUseCase private lateinit var mockRemoveContentFromFavouritesUseCase: RemoveContentFromFavouritesUseCase - private val testScheduler get() = TestMainDispatcher.testDispatcher.scheduler + private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler @BeforeEach fun setUp() { diff --git a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt index 01d59f1..4277a8d 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt @@ -4,7 +4,7 @@ import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi import org.fnives.test.showcase.android.testutil.InstantExecutorExtension import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase -import org.fnives.test.showcase.testutils.TestMainDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -14,13 +14,13 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever -@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class) +@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class) @OptIn(ExperimentalCoroutinesApi::class) internal class SplashViewModelTest { private lateinit var mockIsUserLoggedInUseCase: IsUserLoggedInUseCase private lateinit var sut: SplashViewModel - private val testScheduler get() = TestMainDispatcher.testDispatcher.scheduler + private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler @BeforeEach fun setUp() { diff --git a/codekata/viewmodel.instructionset.md b/codekata/viewmodel.instructionset.md index e7c7f3e..b819eea 100644 --- a/codekata/viewmodel.instructionset.md +++ b/codekata/viewmodel.instructionset.md @@ -25,12 +25,12 @@ Our test class is `org.fnives.test.showcase.ui.splash.CodeKataSplashViewModelTes To properly test LiveData we need to make them instant, meaning as soon as the value is set the observers are updated. To Do this we can use a `InstantExecutorExtension`. -Also We need to set MainDispatcher as TestDispatcher, for this we can use the `TestMainDispatcher` Extension. +Also We need to set MainDispatcher as TestDispatcher, for this we can use the `StandardTestMainDispatcher` Extension. To add this to our TestClass we need to do the following: ```kotlin -@ExtendWith(InstantExecutorExtension::class, TestMainDispatcher::class) +@ExtendWith(InstantExecutorExtension::class, StandardTestMainDispatcher::class) class CodeKataSplashViewModelTest { ``` @@ -41,7 +41,7 @@ Next let's set up our System Under Test as usual: ```kotlin private lateinit var mockIsUserLoggedInUseCase: IsUserLoggedInUseCase private lateinit var sut: SplashViewModel -private val testScheduler get() = TestMainDispatcher.testDispatcher.scheduler // just a shortcut +private val testScheduler get() = StandardTestMainDispatcher.testDispatcher.scheduler // just a shortcut @BeforeEach fun setUp() { @@ -69,7 +69,7 @@ val navigateToTestObserver = sut.navigateTo.test() Since the action takes place in the ViewModel constructor, instead of additional calls, we need to simulate that time has elapsed. -Note: the `TestMainDispatcher` Extension we are using sets `StandardTestDispatcher` as the dispatcher for `Dispatcher.Main`, that's why our test is linear and not shaky. +Note: the `StandardTestMainDispatcher` Extension we are using sets `StandardTestDispatcher` as the dispatcher for `Dispatcher.Main`, that's why our test is linear and not shaky. ```kotlin testScheduler.advanceTimeBy(501) diff --git a/test-util-junit5-android/build.gradle b/test-util-junit5-android/build.gradle index dd13d96..23b7bdc 100644 --- a/test-util-junit5-android/build.gradle +++ b/test-util-junit5-android/build.gradle @@ -32,4 +32,6 @@ android { dependencies { implementation "org.junit.jupiter:junit-jupiter-engine:$junit5_version" implementation "androidx.arch.core:core-runtime:$arch_core_version" + + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" } \ No newline at end of file diff --git a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/StandardTestMainDispatcher.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/StandardTestMainDispatcher.kt new file mode 100644 index 0000000..9fda7c8 --- /dev/null +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/StandardTestMainDispatcher.kt @@ -0,0 +1,30 @@ +package org.fnives.test.showcase.android.testutil + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestDispatcher +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher.Companion.testDispatcher + +/** + * Custom Junit5 Extension which replaces the main dispatcher with a Standard [TestDispatcher] + * + * One can access the test dispatcher via [testDispatcher] static getter. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class StandardTestMainDispatcher : TestMainDispatcher() { + + override var createdTestDispatcher: TestDispatcher? + get() = privateTestDispatcher + set(value) { + privateTestDispatcher = value + } + + override fun createDispatcher(): TestDispatcher = StandardTestDispatcher() + + companion object { + private var privateTestDispatcher: TestDispatcher? = null + val testDispatcher: TestDispatcher + get() = privateTestDispatcher + ?: throw IllegalStateException("StandardTestMainDispatcher is in afterEach State") + } +} diff --git a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt new file mode 100644 index 0000000..c7db54e --- /dev/null +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt @@ -0,0 +1,32 @@ +package org.fnives.test.showcase.android.testutil + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.setMain +import org.junit.jupiter.api.extension.AfterEachCallback +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext + +/** + * Custom Junit5 Extension which replaces the main dispatcher with a [TestDispatcher] + */ +@OptIn(ExperimentalCoroutinesApi::class) +abstract class TestMainDispatcher : BeforeEachCallback, AfterEachCallback { + + protected abstract var createdTestDispatcher: TestDispatcher? + + abstract fun createDispatcher() : TestDispatcher + + final override fun beforeEach(context: ExtensionContext?) { + val testDispatcher = createDispatcher() + createdTestDispatcher = testDispatcher + Dispatchers.setMain(testDispatcher) + } + + final override fun afterEach(context: ExtensionContext?) { + Dispatchers.resetMain() + createdTestDispatcher = null + } +} \ No newline at end of file diff --git a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/UnconfinedTestMainDispatcher.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/UnconfinedTestMainDispatcher.kt new file mode 100644 index 0000000..5113237 --- /dev/null +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/UnconfinedTestMainDispatcher.kt @@ -0,0 +1,30 @@ +package org.fnives.test.showcase.android.testutil + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestDispatcher +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.fnives.test.showcase.android.testutil.UnconfinedTestMainDispatcher.Companion.testDispatcher + +/** + * Custom Junit5 Extension which replaces the main dispatcher with a Unconfined [TestDispatcher] + * + * One can access the test dispatcher via [testDispatcher] static getter. + */ +@OptIn(ExperimentalCoroutinesApi::class) +class UnconfinedTestMainDispatcher : TestMainDispatcher() { + + override var createdTestDispatcher: TestDispatcher? + get() = privateTestDispatcher + set(value) { + privateTestDispatcher = value + } + + override fun createDispatcher(): TestDispatcher = UnconfinedTestDispatcher() + + companion object { + private var privateTestDispatcher: TestDispatcher? = null + val testDispatcher: TestDispatcher + get() = privateTestDispatcher + ?: throw IllegalStateException("StandardTestMainDispatcher is in afterEach State") + } +} From 8ec369f3e7bba11b6f34320aa7c625cd4434e99c Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 11:58:02 +0300 Subject: [PATCH 14/21] PR#98 Fix code analysis errors --- app/src/test/java/org/fnives/test/showcase/di/DITest.kt | 2 +- .../fnives/test/showcase/ui/auth/AuthViewModelTest.kt | 9 ++------- .../test/showcase/ui/auth/CodeKataAuthViewModel.kt | 2 +- .../fnives/test/showcase/ui/home/MainViewModelTest.kt | 2 +- .../test/showcase/ui/splash/SplashViewModelTest.kt | 2 +- gradlescripts/testdependencies.gradle | 2 +- gradlescripts/versions.gradle | 6 +++--- .../android/testutil/InstantExecutorExtension.kt | 3 ++- .../test/showcase/android/testutil/TestMainDispatcher.kt | 4 ++-- 9 files changed, 14 insertions(+), 18 deletions(-) diff --git a/app/src/test/java/org/fnives/test/showcase/di/DITest.kt b/app/src/test/java/org/fnives/test/showcase/di/DITest.kt index bc950e5..087272f 100644 --- a/app/src/test/java/org/fnives/test/showcase/di/DITest.kt +++ b/app/src/test/java/org/fnives/test/showcase/di/DITest.kt @@ -1,8 +1,8 @@ package org.fnives.test.showcase.di import android.content.Context -import org.fnives.test.showcase.model.network.BaseUrl import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher +import org.fnives.test.showcase.model.network.BaseUrl import org.fnives.test.showcase.ui.auth.AuthViewModel import org.fnives.test.showcase.ui.home.MainViewModel import org.fnives.test.showcase.ui.splash.SplashViewModel diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt index 0a1ee75..d1008bf 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/AuthViewModelTest.kt @@ -4,11 +4,11 @@ import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.runBlocking import org.fnives.test.showcase.android.testutil.InstantExecutorExtension +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.core.login.LoginUseCase import org.fnives.test.showcase.model.auth.LoginCredentials import org.fnives.test.showcase.model.auth.LoginStatus import org.fnives.test.showcase.model.shared.Answer -import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName @@ -44,11 +44,6 @@ internal class AuthViewModelTest { @DisplayName("GIVEN initialized viewModel WHEN observed THEN loading false other fields are empty") @Test fun initialSetup() { - var a = "" - repeat(1200 / 2) { - a += "a " - } - System.err.println(a+"b") val usernameTestObserver = sut.username.test() val passwordTestObserver = sut.password.test() val loadingTestObserver = sut.loading.test() @@ -174,7 +169,7 @@ internal class AuthViewModelTest { @ParameterizedTest(name = "GIVEN answer success loginStatus {0} WHEN login called THEN error {1} is shown") fun invalidStatusResultsInErrorState( loginStatus: LoginStatus, - errorType: AuthViewModel.ErrorType + errorType: AuthViewModel.ErrorType, ) { runBlocking { whenever(mockLoginUseCase.invoke(anyOrNull())).doReturn(Answer.Success(loginStatus)) diff --git a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt index 94c7055..3d55565 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/auth/CodeKataAuthViewModel.kt @@ -2,8 +2,8 @@ package org.fnives.test.showcase.ui.auth import kotlinx.coroutines.ExperimentalCoroutinesApi import org.fnives.test.showcase.android.testutil.InstantExecutorExtension -import org.fnives.test.showcase.core.login.LoginUseCase import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher +import org.fnives.test.showcase.core.login.LoginUseCase import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test diff --git a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt index 8e7402f..f4d521c 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/home/MainViewModelTest.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.fnives.test.showcase.android.testutil.InstantExecutorExtension +import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.fnives.test.showcase.core.content.AddContentToFavouriteUseCase import org.fnives.test.showcase.core.content.FetchContentUseCase import org.fnives.test.showcase.core.content.GetAllContentUseCase @@ -15,7 +16,6 @@ import org.fnives.test.showcase.model.content.ContentId import org.fnives.test.showcase.model.content.FavouriteContent import org.fnives.test.showcase.model.content.ImageUrl import org.fnives.test.showcase.model.shared.Resource -import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test diff --git a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt index 4277a8d..adcd7c7 100644 --- a/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt +++ b/app/src/test/java/org/fnives/test/showcase/ui/splash/SplashViewModelTest.kt @@ -3,8 +3,8 @@ package org.fnives.test.showcase.ui.splash import com.jraska.livedata.test import kotlinx.coroutines.ExperimentalCoroutinesApi import org.fnives.test.showcase.android.testutil.InstantExecutorExtension -import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher +import org.fnives.test.showcase.core.login.IsUserLoggedInUseCase import org.fnives.test.showcase.ui.shared.Event import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle index 05ed31e..34c0afb 100644 --- a/gradlescripts/testdependencies.gradle +++ b/gradlescripts/testdependencies.gradle @@ -37,7 +37,7 @@ project.ext { // ------------------VERSIONS------------------ def testing_junit5_version = propertyOrNull('junit5_version') ?: "5.7.0" - def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.12" + def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.13.2" def testing_robolectric_version = propertyOrNull('robolectric_version') ?: "4.7" def testing_androidx_code_version = propertyOrNull('androidx_test_version') ?: "1.4.0" def testing_androidx_junit_version = propertyOrNull('androidx_junit_version') ?: "1.1.3" diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index 5362774..42bc419 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -1,7 +1,7 @@ project.ext { - androidx_core_version = "1.7.0" + androidx_core_version = "1.8.0" androidx_appcompat_version = "1.4.1" - androidx_material_version = "1.5.0" + androidx_material_version = "1.6.1" androidx_constraintlayout_version = "2.1.3" androidx_livedata_version = "2.4.0" androidx_swiperefreshlayout_version = "1.1.0" @@ -28,7 +28,7 @@ project.ext { testing_kotlin_mockito_version = "4.0.0" junit5_version = "5.7.0" testing_json_assert_version = "1.5.0" - testing_junit4_version = "4.12" + testing_junit4_version = "4.13.2" testing_robolectric_version = "4.7" espresso_version = "3.4.0" } \ No newline at end of file diff --git a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt index a3b109e..f64f387 100644 --- a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/InstantExecutorExtension.kt @@ -12,7 +12,8 @@ import org.junit.jupiter.api.extension.ExtensionContext * * reference: https://developer.android.com/reference/androidx/arch/core/executor/testing/InstantTaskExecutorRule * - * A JUnit5 Extensions that swaps the background executor used by the Architecture Components with a different one which executes each task synchronously. + * A JUnit5 Extensions that swaps the background executor used by the Architecture Components with a different + * one which executes each task synchronously. * You can use this extension for your host side tests that use Architecture Components. */ @SuppressLint("RestrictedApi") diff --git a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt index c7db54e..265e0ec 100644 --- a/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt +++ b/test-util-junit5-android/src/main/java/org/fnives/test/showcase/android/testutil/TestMainDispatcher.kt @@ -17,7 +17,7 @@ abstract class TestMainDispatcher : BeforeEachCallback, AfterEachCallback { protected abstract var createdTestDispatcher: TestDispatcher? - abstract fun createDispatcher() : TestDispatcher + abstract fun createDispatcher(): TestDispatcher final override fun beforeEach(context: ExtensionContext?) { val testDispatcher = createDispatcher() @@ -29,4 +29,4 @@ abstract class TestMainDispatcher : BeforeEachCallback, AfterEachCallback { Dispatchers.resetMain() createdTestDispatcher = null } -} \ No newline at end of file +} From b8b2e0e29eaf76d525e2097166555e94ae2d1c2b Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 12:13:38 +0300 Subject: [PATCH 15/21] PR#98 Fix versions read from wrong extensions and remove BuildConfigs --- gradlescripts/testdependencies.gradle | 12 ++++++------ gradlescripts/versions.gradle | 15 +++++++++------ mockserver/build.gradle | 4 ++-- test-util-android/build.gradle | 3 +++ test-util-junit5-android/build.gradle | 3 +++ test-util-shared-android/build.gradle | 3 +++ test-util-shared-robolectric/build.gradle | 3 +++ 7 files changed, 29 insertions(+), 14 deletions(-) diff --git a/gradlescripts/testdependencies.gradle b/gradlescripts/testdependencies.gradle index 34c0afb..f4ce4b8 100644 --- a/gradlescripts/testdependencies.gradle +++ b/gradlescripts/testdependencies.gradle @@ -28,8 +28,8 @@ project.ext { */ def propertyOrNull = { key -> - if (hasProperty(key)) { - return property(key) + if (extensions.extraProperties.has(key)) { + return extensions.extraProperties.get(key) } else { return null } @@ -37,22 +37,22 @@ project.ext { // ------------------VERSIONS------------------ def testing_junit5_version = propertyOrNull('junit5_version') ?: "5.7.0" - def testing_junit4_version = propertyOrNull('juni4_version') ?: "4.13.2" + def testing_junit4_version = propertyOrNull('junit4_version') ?: "4.13.2" def testing_robolectric_version = propertyOrNull('robolectric_version') ?: "4.7" def testing_androidx_code_version = propertyOrNull('androidx_test_version') ?: "1.4.0" def testing_androidx_junit_version = propertyOrNull('androidx_junit_version') ?: "1.1.3" def testing_espresso_version = propertyOrNull('espresso_version') ?: "3.4.0" def testing_androidx_arch_core_version = propertyOrNull('arch_core_version') ?: "2.1.0" def test_coroutines_version = propertyOrNull('coroutines_version') ?: "1.6.0" - def testing_kotlin_mockito_version = propertyOrNull('mockito_version') ?: "3.1.0" + def testing_kotlin_mockito_version = propertyOrNull('mockito_version') ?: "4.0.0" def testing_koin_version = propertyOrNull('koin_version') ?: "3.1.2" def testing_json_assert_version = propertyOrNull('json_assert_version') ?: "1.5.0" def testing_okhttp3 = propertyOrNull('okhttp_version') ?: "4.9.3" def testing_turbine_version = propertyOrNull('turbine_version') ?: "0.7.0" def testing_androidx_room_version = propertyOrNull('room_version') ?: "2.4.2" def testing_livedata_version = propertyOrNull('testing_livedata_version') ?: "1.2.0" - def testing_compose_version = propertyOrNull('compose_version') ?: "1.1.0" - def testing_hamcrest_version = propertyOrNull('hamcrest_version')?: "2.2" + def testing_compose_version = propertyOrNull('androidx_compose_version') ?: "1.1.0" + def testing_hamcrest_version = propertyOrNull('hamcrest_version') ?: "2.2" // ------------------PRIVATE------------------ // JUni4 + Espresso + Room diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index 42bc419..7c1f9da 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -14,21 +14,24 @@ project.ext { androidx_compose_constraintlayout_version = "1.0.0" coroutines_version = "1.6.0" - turbine_version = "0.7.0" koin_version = "3.1.2" coil_version = "1.4.0" retrofit_version = "2.9.0" okhttp_version = "4.9.3" moshi_version = "1.13.0" + // testing versions + turbine_version = "0.7.0" androidx_test_version = "1.4.0" - testing_androidx_junit_version = "1.1.3" + androidx_junit_version = "1.1.3" arch_core_version = "2.1.0" testing_livedata_version = "1.2.0" - testing_kotlin_mockito_version = "4.0.0" + mockito_version = "4.0.0" + println("MYLOG versions set") junit5_version = "5.7.0" - testing_json_assert_version = "1.5.0" - testing_junit4_version = "4.13.2" - testing_robolectric_version = "4.7" + json_assert_version = "1.5.0" + junit4_version = "4.13.2" + robolectric_version = "4.7" espresso_version = "3.4.0" + hamcrest_version = "2.2" } \ No newline at end of file diff --git a/mockserver/build.gradle b/mockserver/build.gradle index 4b481ef..03f8e01 100644 --- a/mockserver/build.gradle +++ b/mockserver/build.gradle @@ -14,7 +14,7 @@ dependencies { api "com.squareup.okhttp3:mockwebserver:$okhttp_version" api "com.squareup.okhttp3:okhttp-tls:$okhttp_version" - implementation "org.skyscreamer:jsonassert:$testing_json_assert_version" - implementation "junit:junit:$testing_junit4_version" + implementation "org.skyscreamer:jsonassert:$json_assert_version" + implementation "junit:junit:$junit4_version" } \ No newline at end of file diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 6530663..4f1819a 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -27,6 +27,9 @@ android { kotlinOptions { jvmTarget = '1.8' } + buildFeatures { + buildConfig = false + } } dependencies { diff --git a/test-util-junit5-android/build.gradle b/test-util-junit5-android/build.gradle index 23b7bdc..f700de7 100644 --- a/test-util-junit5-android/build.gradle +++ b/test-util-junit5-android/build.gradle @@ -27,6 +27,9 @@ android { kotlinOptions { jvmTarget = '1.8' } + buildFeatures { + buildConfig = false + } } dependencies { diff --git a/test-util-shared-android/build.gradle b/test-util-shared-android/build.gradle index bf17210..36a2f69 100644 --- a/test-util-shared-android/build.gradle +++ b/test-util-shared-android/build.gradle @@ -27,6 +27,9 @@ android { kotlinOptions { jvmTarget = '1.8' } + buildFeatures { + buildConfig = false + } } dependencies { diff --git a/test-util-shared-robolectric/build.gradle b/test-util-shared-robolectric/build.gradle index ed8dde6..8bcb73a 100644 --- a/test-util-shared-robolectric/build.gradle +++ b/test-util-shared-robolectric/build.gradle @@ -27,6 +27,9 @@ android { kotlinOptions { jvmTarget = '1.8' } + buildFeatures { + buildConfig = false + } } dependencies { From 6156e9457f48c3b20b8c85c9015959ba3ab6dcd5 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 12:58:01 +0300 Subject: [PATCH 16/21] Issue#67 Define deployment of libraries to GitHub --- gradle.properties | 3 +- gradlescripts/deploy.aar.gradle | 44 +++++++++++++++++++++++ gradlescripts/versions.gradle | 1 - test-util-android/build.gradle | 5 ++- test-util-junit5-android/build.gradle | 5 ++- test-util-shared-android/build.gradle | 4 ++- test-util-shared-robolectric/build.gradle | 5 ++- 7 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 gradlescripts/deploy.aar.gradle diff --git a/gradle.properties b/gradle.properties index dbc9506..c3dcb2c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,4 +18,5 @@ android.useAndroidX=true # Automatically convert third-party libraries to use AndroidX android.enableJetifier=false # Kotlin code style for this project: "official" or "obsolete": -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +android.disableAutomaticComponentCreation=true diff --git a/gradlescripts/deploy.aar.gradle b/gradlescripts/deploy.aar.gradle new file mode 100644 index 0000000..6ab3795 --- /dev/null +++ b/gradlescripts/deploy.aar.gradle @@ -0,0 +1,44 @@ +apply plugin: "maven-publish" + +def testUtilVersion = "1.0.0" +if (!extensions.extraProperties.has("artifactId")) { + throw IllegalStateException("ext.artifactId is not set while applying deploy script") +} +def testUtilGroupId = "org.fnives.android.testutil" +def testUtilArtifactId = extensions.extraProperties.get("artifactId") + +task publishToGitHub(dependsOn: "publishMavenAarPublicationToGitHubPackagesRepository") { + group = "Publishing" +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier "sources" +} + +afterEvaluate { + publishing { + publications { + mavenAar(MavenPublication) { + from components.release + + groupId "$testUtilGroupId" + println("$testUtilArtifactId") + version "$testUtilVersion" + artifactId "$testUtilArtifactId" + artifact sourcesJar + } + } + + repositories { + maven { + name = "GitHubPackages" + url = uri("https://maven.pkg.github.com/fknives/AndroidTest-ShowCase") + credentials { + username = System.getenv("GITHUB_USERNAME") + password = System.getenv("GITHUB_TOKEN") + } + } + } + } +} \ No newline at end of file diff --git a/gradlescripts/versions.gradle b/gradlescripts/versions.gradle index 7c1f9da..ff127d6 100644 --- a/gradlescripts/versions.gradle +++ b/gradlescripts/versions.gradle @@ -27,7 +27,6 @@ project.ext { arch_core_version = "2.1.0" testing_livedata_version = "1.2.0" mockito_version = "4.0.0" - println("MYLOG versions set") junit5_version = "5.7.0" json_assert_version = "1.5.0" junit4_version = "4.13.2" diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 4f1819a..160e628 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -44,4 +44,7 @@ dependencies { implementation "com.google.android.material:material:$androidx_material_version" implementation "androidx.swiperefreshlayout:swiperefreshlayout:$androidx_swiperefreshlayout_version" implementation "androidx.core:core-ktx:$androidx_core_version" -} \ No newline at end of file +} + +ext.artifactId = "android" +apply from: "../gradlescripts/deploy.aar.gradle" \ No newline at end of file diff --git a/test-util-junit5-android/build.gradle b/test-util-junit5-android/build.gradle index f700de7..6e2f11a 100644 --- a/test-util-junit5-android/build.gradle +++ b/test-util-junit5-android/build.gradle @@ -37,4 +37,7 @@ dependencies { implementation "androidx.arch.core:core-runtime:$arch_core_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" -} \ No newline at end of file +} + +ext.artifactId = "android-unit-junit5" +apply from: "../gradlescripts/deploy.aar.gradle" \ No newline at end of file diff --git a/test-util-shared-android/build.gradle b/test-util-shared-android/build.gradle index 36a2f69..1e4f84f 100644 --- a/test-util-shared-android/build.gradle +++ b/test-util-shared-android/build.gradle @@ -35,5 +35,7 @@ android { dependencies { implementation "androidx.room:room-testing:$room_version" api project(':test-util-shared-robolectric') +} -} \ No newline at end of file +ext.artifactId = "shared-android" +apply from: "../gradlescripts/deploy.aar.gradle" \ No newline at end of file diff --git a/test-util-shared-robolectric/build.gradle b/test-util-shared-robolectric/build.gradle index 8bcb73a..b6cc483 100644 --- a/test-util-shared-robolectric/build.gradle +++ b/test-util-shared-robolectric/build.gradle @@ -35,4 +35,7 @@ android { dependencies { implementation "androidx.room:room-testing:$room_version" implementation "androidx.arch.core:core-testing:$arch_core_version" -} \ No newline at end of file +} + +ext.artifactId = "shared-robolectric" +apply from: "../gradlescripts/deploy.aar.gradle" \ No newline at end of file From fad19ffee7ab331bffde96301634c60a61ec9c65 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 13:01:29 +0300 Subject: [PATCH 17/21] Issue#67 Test published dependencies --- app/build.gradle | 15 ++++++++++----- build.gradle | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 85095b1..15a4ea3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -107,11 +107,16 @@ dependencies { androidTestImplementation project(':mockserver') testImplementation testFixtures(project(':core')) - testImplementation project(':test-util-junit5-android') +// testImplementation project(':test-util-junit5-android') + testImplementation "org.fnives.android.testutil:android-unit-junit5:1.0.0" androidTestImplementation testFixtures(project(':core')) - testImplementation project(':test-util-shared-robolectric') - androidTestImplementation project(':test-util-shared-android') - testImplementation project(':test-util-android') - androidTestImplementation project(':test-util-android') +// testImplementation project(':test-util-shared-robolectric') + testImplementation "org.fnives.android.testutil:shared-robolectric:1.0.0" +// androidTestImplementation project(':test-util-shared-android') + androidTestImplementation "org.fnives.android.testutil:shared-android:1.0.0" +// testImplementation project(':test-util-android') + testImplementation "org.fnives.android.testutil:android:1.0.0" +// androidTestImplementation project(':test-util-android') + androidTestImplementation "org.fnives.android.testutil:android:1.0.0" } diff --git a/build.gradle b/build.gradle index 0e88155..b40d313 100644 --- a/build.gradle +++ b/build.gradle @@ -22,6 +22,14 @@ allprojects { repositories { mavenCentral() google() + maven { + url "https://maven.pkg.github.com/fknives/AndroidTest-ShowCase" + credentials { + username = project.findProperty("GITHUB_USERNAME") ?: System.getenv("GITHUB_USERNAME") + password = project.findProperty("GITHUB_TOKEN") ?: System.getenv("GITHUB_TOKEN") + } + // https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token + } } } From ca9c813121f1b011927b0a2c74a8e46e1736c073 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 14:38:53 +0300 Subject: [PATCH 18/21] Issue#67 Update Readme with test util module releases --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ app/build.gradle | 16 ++++++-------- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7063de8..9ccd47a 100644 --- a/README.md +++ b/README.md @@ -170,5 +170,59 @@ Open the [shared tests instruction set](./codekata/sharedtests.instructionset.md In this section we will see how can we share Robolectric test source with AndroidTests to run our same tests on actual device. We will also see how to write AndroidTest End to End Tests. + +## Util classes + +Additional modules have been added prefixed with test-util. + +These contain all the reusable Test Util classes used in the showcase. + +The Testing setup is extracted into a separate gradle script, which with some modifications, you should be able to easily add to your own project. + +To use the TestUtil classes, you will need to add the GitHub Repository as a source for dependencies: + +```groovy +// top level build.gradle +allprojects { + repositories { + // ... + maven { + url "https://maven.pkg.github.com/fknives/AndroidTest-ShowCase" + credentials { + username = project.findProperty("GITHUB_USERNAME") ?: System.getenv("GITHUB_USERNAME") + password = project.findProperty("GITHUB_TOKEN") ?: System.getenv("GITHUB_TOKEN") + } + // https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token + } + } +} +// OR +// top level build.gradle.kts +allprojects { + repositories { + // ... + maven { + url = uri("https://maven.pkg.github.com/fknives/AndroidTest-ShowCase") + credentials { + username = extra.properties["GITHUB_USERNAME"] as String? ?: System.getenv("GITHUB_USERNAME") + password = extra.properties["GITHUB_TOKEN"] as String? ?: System.getenv("GITHUB_TOKEN") + } + // https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token + } + } +} +``` + +*Latest version:*![Latest release](https://img.shields.io/github/v/release/fknives/AndroidTest-ShowCase) + +and then you can use the following dependencies: +```groovy +testImplementation "org.fnives.android.testutil:android-unit-junit5:" // test-util-junit5-android +testImplementation "org.fnives.android.testutil:shared-robolectric:" // test-util-shared-robolectric +testImplementation "org.fnives.android.testutil:android:" // test-util-android +androidTestImplementation "org.fnives.android.testutil:android:" // test-util-android +androidTestImplementation "org.fnives.android.testutil:shared-android:" // test-util-shared-android +``` + ## License [License file](./LICENSE) diff --git a/app/build.gradle b/app/build.gradle index 15a4ea3..d5da099 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -106,17 +106,13 @@ dependencies { androidTestImplementation project(':mockserver') + testImplementation project(':test-util-junit5-android') + testImplementation project(':test-util-shared-robolectric') + testImplementation project(':test-util-android') + androidTestImplementation project(':test-util-android') + androidTestImplementation project(':test-util-shared-android') + testImplementation testFixtures(project(':core')) -// testImplementation project(':test-util-junit5-android') - testImplementation "org.fnives.android.testutil:android-unit-junit5:1.0.0" androidTestImplementation testFixtures(project(':core')) -// testImplementation project(':test-util-shared-robolectric') - testImplementation "org.fnives.android.testutil:shared-robolectric:1.0.0" -// androidTestImplementation project(':test-util-shared-android') - androidTestImplementation "org.fnives.android.testutil:shared-android:1.0.0" -// testImplementation project(':test-util-android') - testImplementation "org.fnives.android.testutil:android:1.0.0" -// androidTestImplementation project(':test-util-android') - androidTestImplementation "org.fnives.android.testutil:android:1.0.0" } From c4c4067391813e93a3aa9c0ae92e8e689263932b Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 14:40:45 +0300 Subject: [PATCH 19/21] Issue#67 Add pipeline to release packages --- .github/workflows/release-package.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/workflows/release-package.yml diff --git a/.github/workflows/release-package.yml b/.github/workflows/release-package.yml new file mode 100644 index 0000000..1af5df1 --- /dev/null +++ b/.github/workflows/release-package.yml @@ -0,0 +1,27 @@ +name: Library Package Publishing + +on: + release: + types: + - created + +env: + GITHUB_USERNAME: "fknives" + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + publish-library: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Java + uses: actions/setup-java@v2 + with: + distribution: 'adopt' + java-version: '11' + - name: Publish Project + run: ./gradlew publishToGitHub \ No newline at end of file From cec6c7e1018aa07245f0a681bfa65741bf1063c1 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 14:41:17 +0300 Subject: [PATCH 20/21] Issue#67 Update version to 1.0.1 --- gradlescripts/deploy.aar.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradlescripts/deploy.aar.gradle b/gradlescripts/deploy.aar.gradle index 6ab3795..fcc77df 100644 --- a/gradlescripts/deploy.aar.gradle +++ b/gradlescripts/deploy.aar.gradle @@ -1,6 +1,6 @@ apply plugin: "maven-publish" -def testUtilVersion = "1.0.0" +def testUtilVersion = "1.0.1" if (!extensions.extraProperties.has("artifactId")) { throw IllegalStateException("ext.artifactId is not set while applying deploy script") } From fe9f85dc27f9f2a9206ed319032a2d5d319cd0fe Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Tue, 12 Jul 2022 15:29:57 +0300 Subject: [PATCH 21/21] PR#98 Exclude meta informations --- test-util-android/build.gradle | 6 ++++++ test-util-junit5-android/build.gradle | 6 ++++++ test-util-shared-android/build.gradle | 6 ++++++ test-util-shared-robolectric/build.gradle | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/test-util-android/build.gradle b/test-util-android/build.gradle index 160e628..c379b2b 100644 --- a/test-util-android/build.gradle +++ b/test-util-android/build.gradle @@ -30,6 +30,12 @@ android { buildFeatures { buildConfig = false } + packagingOptions { + exclude 'META-INF/LGPL2.1' + exclude 'META-INF/AL2.0' + exclude 'META-INF/LICENSE.md' + exclude 'META-INF/LICENSE-notice.md' + } } dependencies { diff --git a/test-util-junit5-android/build.gradle b/test-util-junit5-android/build.gradle index 6e2f11a..2809993 100644 --- a/test-util-junit5-android/build.gradle +++ b/test-util-junit5-android/build.gradle @@ -30,6 +30,12 @@ android { buildFeatures { buildConfig = false } + packagingOptions { + exclude 'META-INF/LGPL2.1' + exclude 'META-INF/AL2.0' + exclude 'META-INF/LICENSE.md' + exclude 'META-INF/LICENSE-notice.md' + } } dependencies { diff --git a/test-util-shared-android/build.gradle b/test-util-shared-android/build.gradle index 1e4f84f..bb2ffbc 100644 --- a/test-util-shared-android/build.gradle +++ b/test-util-shared-android/build.gradle @@ -30,6 +30,12 @@ android { buildFeatures { buildConfig = false } + packagingOptions { + exclude 'META-INF/LGPL2.1' + exclude 'META-INF/AL2.0' + exclude 'META-INF/LICENSE.md' + exclude 'META-INF/LICENSE-notice.md' + } } dependencies { diff --git a/test-util-shared-robolectric/build.gradle b/test-util-shared-robolectric/build.gradle index b6cc483..db2c58d 100644 --- a/test-util-shared-robolectric/build.gradle +++ b/test-util-shared-robolectric/build.gradle @@ -30,6 +30,12 @@ android { buildFeatures { buildConfig = false } + packagingOptions { + exclude 'META-INF/LGPL2.1' + exclude 'META-INF/AL2.0' + exclude 'META-INF/LICENSE.md' + exclude 'META-INF/LICENSE-notice.md' + } } dependencies {