Issue#67 Extract MigrationTestHelper into separate module

This commit is contained in:
Gergely Hegedus 2022-04-12 14:37:39 +03:00
parent f35fe810e0
commit 4932b4b2e0
26 changed files with 249 additions and 225 deletions

View file

@ -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')
}

View file

@ -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<out RoomDatabase>
): SharedMigrationTestRule =
AndroidMigrationTestRule(
instrumentation,
databaseClass
)
override fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>
): SharedMigrationTestRule =
AndroidMigrationTestRule(
instrumentation,
databaseClass,
specs
)
override fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>,
openFactory: SupportSQLiteOpenHelper.Factory
): SharedMigrationTestRule =
AndroidMigrationTestRule(
instrumentation,
databaseClass,
specs,
openFactory,
)
}

View file

@ -1,7 +0,0 @@
package org.fnives.test.showcase.testutils.configuration
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
AndroidMigrationTestRuleFactory
}

View file

@ -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<out RoomDatabase>
): SharedMigrationTestRule =
RobolectricMigrationTestHelper(
instrumentation,
databaseClass
)
override fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>
): SharedMigrationTestRule =
RobolectricMigrationTestHelper(
instrumentation,
databaseClass,
specs
)
override fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>,
openFactory: SupportSQLiteOpenHelper.Factory
): SharedMigrationTestRule =
RobolectricMigrationTestHelper(
instrumentation,
databaseClass,
specs,
openFactory
)
}

View file

@ -1,7 +0,0 @@
package org.fnives.test.showcase.testutils.configuration
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
RobolectricMigrationTestHelperFactory
}

View file

@ -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<LocalDatabase>(
InstrumentationRegistry.getInstrumentation(),
emptyList(),
FrameworkSQLiteOpenHelperFactory()
)
val helper = SharedMigrationTestRule<LocalDatabase>(instrumentation = InstrumentationRegistry.getInstrumentation())
private fun getMigratedRoomDatabase(): LocalDatabase {
val database: LocalDatabase = Room.databaseBuilder(

View file

@ -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 <reified DB : RoomDatabase> createSharedMigrationTestRule(
instrumentation: Instrumentation
): SharedMigrationTestRule =
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
.createSharedMigrationTestRule(
instrumentation,
DB::class.java
)
inline fun <reified DB : RoomDatabase> createSharedMigrationTestRule(
instrumentation: Instrumentation,
specs: List<AutoMigrationSpec>
): SharedMigrationTestRule =
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
.createSharedMigrationTestRule(
instrumentation,
DB::class.java,
specs
)
inline fun <reified DB : RoomDatabase> createSharedMigrationTestRule(
instrumentation: Instrumentation,
specs: List<AutoMigrationSpec>,
openFactory: SupportSQLiteOpenHelper.Factory
): SharedMigrationTestRule =
SpecificTestConfigurationsFactory.createSharedMigrationTestRuleFactory()
.createSharedMigrationTestRule(
instrumentation,
DB::class.java,
specs,
openFactory
)

View file

@ -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<out RoomDatabase>,
): SharedMigrationTestRule
fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>
): SharedMigrationTestRule
fun createSharedMigrationTestRule(
instrumentation: Instrumentation,
databaseClass: Class<out RoomDatabase>,
specs: List<AutoMigrationSpec>,
openFactory: SupportSQLiteOpenHelper.Factory
): SharedMigrationTestRule
}

View file

@ -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() {

View file

@ -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
}

View file

@ -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

View file

@ -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",
]

View file

@ -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"
}

View file

@ -1,6 +1,8 @@
rootProject.name = "TestShowCase"
include ':mockserver'
include ':model'
include ':core'
include ':network'
include ':app'
rootProject.name = "TestShowCase"
include ':test-util-shared-android'
include ':test-util-shared-robolectric'

1
test-util-shared-android/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -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')
}

View file

@ -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

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fnives.test.showcase.android.testutil">
</manifest>

View file

@ -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<AutoMigrationSpec>,
openFactory: SupportSQLiteOpenHelper.Factory
) {
migrationTestHelper =
MigrationTestHelper(instrumentation, databaseClass, specs, openFactory)
migrationTestHelper = MigrationTestHelper(instrumentation, databaseClass, specs, openFactory)
}
override fun apply(base: Statement, description: Description): Statement =

View file

@ -0,0 +1 @@
/build

View file

@ -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"
}

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.fnives.test.showcase.android.testutil.robolectric">
</manifest>

View file

@ -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)
}

View file

@ -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 <reified Database: RoomDatabase> 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 <reified Database: RoomDatabase> SharedMigrationTestRule(
instrumentation: Instrumentation,
specs: List<AutoMigrationSpec>
): 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 <reified Database: RoomDatabase> SharedMigrationTestRule(
instrumentation: Instrumentation,
specs: List<AutoMigrationSpec>,
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
}

View file

@ -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<WeakReference<SupportSQLiteDatabase>> 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<? extends RoomDatabase> 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<? extends RoomDatabase> databaseClass,
@NonNull List<AutoMigrationSpec> 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<? extends RoomDatabase> databaseClass,
@NonNull List<AutoMigrationSpec> specs,
@NonNull SupportSQLiteOpenHelper.Factory openFactory