commit
45bcd20b2a
88 changed files with 1082 additions and 549 deletions
27
.github/workflows/release-package.yml
vendored
Normal file
27
.github/workflows/release-package.yml
vendored
Normal file
|
|
@ -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
|
||||
54
README.md
54
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:*
|
||||
|
||||
and then you can use the following dependencies:
|
||||
```groovy
|
||||
testImplementation "org.fnives.android.testutil:android-unit-junit5:<latestVersion>" // test-util-junit5-android
|
||||
testImplementation "org.fnives.android.testutil:shared-robolectric:<latestVersion>" // test-util-shared-robolectric
|
||||
testImplementation "org.fnives.android.testutil:android:<latestVersion>" // test-util-android
|
||||
androidTestImplementation "org.fnives.android.testutil:android:<latestVersion>" // test-util-android
|
||||
androidTestImplementation "org.fnives.android.testutil:shared-android:<latestVersion>" // test-util-shared-android
|
||||
```
|
||||
|
||||
## License
|
||||
[License file](./LICENSE)
|
||||
|
|
|
|||
|
|
@ -92,55 +92,25 @@ 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"
|
||||
|
||||
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 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'))
|
||||
androidTestImplementation testFixtures(project(':core'))
|
||||
|
|
|
|||
|
|
@ -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.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.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.testutils.storage.TestDatabaseInitialization
|
||||
import org.fnives.test.showcase.ui.splash.SplashActivity
|
||||
import org.hamcrest.Description
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
||||
|
||||
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
|
||||
AndroidMigrationTestRuleFactory
|
||||
}
|
||||
|
|
@ -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.DispatcherTestRule
|
||||
import org.fnives.test.showcase.testutils.idling.anyResourceIdling
|
||||
import org.fnives.test.showcase.testutils.idling.DatabaseDispatcherTestRule
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package org.fnives.test.showcase.testutils.configuration
|
||||
|
||||
object SpecificTestConfigurationsFactory : TestConfigurationsFactory {
|
||||
|
||||
override fun createSharedMigrationTestRuleFactory(): SharedMigrationTestRuleFactory =
|
||||
RobolectricMigrationTestHelperFactory
|
||||
}
|
||||
|
|
@ -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.testutils.safeClose
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package org.fnives.test.showcase.testutils
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
fun runOnUIAwaitOnCurrent(action: () -> Unit) {
|
||||
if (Looper.myLooper() === Looper.getMainLooper()) {
|
||||
action()
|
||||
} else {
|
||||
val deferred = CompletableDeferred<Unit>()
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
action()
|
||||
deferred.complete(Unit)
|
||||
}
|
||||
runBlocking { deferred.await() }
|
||||
}
|
||||
}
|
||||
|
|
@ -3,14 +3,16 @@ 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.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
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DispatcherTestRule : TestRule {
|
||||
class DatabaseDispatcherTestRule : TestRule {
|
||||
|
||||
private lateinit var testDispatcher: TestDispatcher
|
||||
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
|
||||
interface Disposable {
|
||||
val isDisposed: Boolean
|
||||
fun dispose()
|
||||
}
|
||||
|
|
@ -1,58 +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.testutils.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.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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.testutils.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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ 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.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
|
||||
|
|
@ -10,9 +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.testutils.idling.loopMainThreadFor
|
||||
import org.fnives.test.showcase.testutils.idling.loopMainThreadUntilIdleWithIdlingResources
|
||||
import org.fnives.test.showcase.testutils.safeClose
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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.testutils.safeClose
|
||||
import org.fnives.test.showcase.ui.auth.AuthActivity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.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
|
||||
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -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.testutils.safeClose
|
||||
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogin
|
||||
import org.fnives.test.showcase.testutils.statesetup.SetupAuthenticationState.setupLogout
|
||||
import org.junit.After
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package org.fnives.test.showcase.di
|
||||
|
||||
import android.content.Context
|
||||
import org.fnives.test.showcase.android.testutil.StandardTestMainDispatcher
|
||||
import org.fnives.test.showcase.model.network.BaseUrl
|
||||
import org.fnives.test.showcase.testutils.TestMainDispatcher
|
||||
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<AuthViewModel>()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,12 @@ 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.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.testutils.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.testutils.TestMainDispatcher
|
||||
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() {
|
||||
|
|
@ -169,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))
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
package org.fnives.test.showcase.ui.auth
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
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.testutils.InstantExecutorExtension
|
||||
import org.fnives.test.showcase.testutils.TestMainDispatcher
|
||||
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() {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ 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.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
|
||||
|
|
@ -14,22 +16,20 @@ 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
|
||||
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
|
||||
|
||||
@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() {
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ 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.android.testutil.StandardTestMainDispatcher
|
||||
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
|
||||
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() {
|
||||
|
|
|
|||
10
build.gradle
10
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,3 +42,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'
|
||||
apply from: 'gradlescripts/testdependencies.gradle'
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@ android.useAndroidX=true
|
|||
android.enableJetifier=false
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
android.disableAutomaticComponentCreation=true
|
||||
|
|
|
|||
44
gradlescripts/deploy.aar.gradle
Normal file
44
gradlescripts/deploy.aar.gradle
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
apply plugin: "maven-publish"
|
||||
|
||||
def testUtilVersion = "1.0.1"
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
gradlescripts/test.tasks.gradle
Normal file
16
gradlescripts/test.tasks.gradle
Normal file
|
|
@ -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'
|
||||
}
|
||||
148
gradlescripts/testdependencies.gradle
Normal file
148
gradlescripts/testdependencies.gradle
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
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 (extensions.extraProperties.has(key)) {
|
||||
return extensions.extraProperties.get(key)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------VERSIONS------------------
|
||||
def testing_junit5_version = propertyOrNull('junit5_version') ?: "5.7.0"
|
||||
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') ?: "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('androidx_compose_version') ?: "1.1.0"
|
||||
def testing_hamcrest_version = propertyOrNull('hamcrest_version') ?: "2.2"
|
||||
|
||||
// ------------------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",
|
||||
"org.hamcrest:hamcrest:$testing_hamcrest_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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -63,20 +63,3 @@ 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'
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
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"
|
||||
androidx_room_version = "2.4.1"
|
||||
room_version = "2.4.2"
|
||||
activity_ktx_version = "1.4.0"
|
||||
androidx_navigation = "2.4.0"
|
||||
|
||||
|
|
@ -14,21 +14,23 @@ 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_androidx_code_version = "1.4.0"
|
||||
testing_androidx_junit_version = "1.1.3"
|
||||
testing_androidx_arch_core_version = "2.1.0"
|
||||
// testing versions
|
||||
turbine_version = "0.7.0"
|
||||
androidx_test_version = "1.4.0"
|
||||
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"
|
||||
testing_junit5_version = "5.7.0"
|
||||
testing_json_assert_version = "1.5.0"
|
||||
testing_junit4_version = "4.13.2"
|
||||
testing_robolectric_version = "4.7"
|
||||
testing_espresso_version = "3.4.0"
|
||||
mockito_version = "4.0.0"
|
||||
junit5_version = "5.7.0"
|
||||
json_assert_version = "1.5.0"
|
||||
junit4_version = "4.13.2"
|
||||
robolectric_version = "4.7"
|
||||
espresso_version = "3.4.0"
|
||||
hamcrest_version = "2.2"
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
||||
}
|
||||
|
|
@ -24,12 +24,9 @@ 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"
|
||||
testFixturesApi "org.junit.jupiter:junit-jupiter-engine:$junit5_version"
|
||||
testFixturesApi "io.insert-koin:koin-test-junit5:$koin_version"
|
||||
}
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
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'
|
||||
include ':test-util-android'
|
||||
include ':test-util-junit5-android'
|
||||
|
|
|
|||
1
test-util-android/.gitignore
vendored
Normal file
1
test-util-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
56
test-util-android/build.gradle
Normal file
56
test-util-android/build.gradle
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
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'
|
||||
}
|
||||
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 {
|
||||
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-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"
|
||||
}
|
||||
|
||||
ext.artifactId = "android"
|
||||
apply from: "../gradlescripts/deploy.aar.gradle"
|
||||
0
test-util-android/consumer-rules.pro
Normal file
0
test-util-android/consumer-rules.pro
Normal file
21
test-util-android/proguard-rules.pro
vendored
Normal file
21
test-util-android/proguard-rules.pro
vendored
Normal 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
|
||||
5
test-util-android/src/main/AndroidManifest.xml
Normal file
5
test-util-android/src/main/AndroidManifest.xml
Normal 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>
|
||||
|
|
@ -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 <T : Activity> ActivityScenario<T>.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.
|
||||
*/
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
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
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.StringDescription
|
||||
|
||||
@Suppress("SwallowedException")
|
||||
fun notIntended(matcher: Matcher<Intent>) {
|
||||
try {
|
||||
intended(matcher)
|
||||
|
|
@ -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,22 +9,25 @@ 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.snackbar.Snackbar
|
||||
import org.hamcrest.Matcher
|
||||
import org.hamcrest.Matchers
|
||||
import com.google.android.material.R as MaterialR
|
||||
|
||||
class SnackbarVerificationHelper {
|
||||
object SnackbarVerificationHelper {
|
||||
|
||||
fun assertIsShownWithText(@StringRes stringResID: Int) {
|
||||
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)))
|
||||
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() {
|
||||
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 {
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
package org.fnives.test.showcase.android.testutil.synchronization.idlingresources
|
||||
|
||||
class CompositeDisposable(disposable: List<Disposable> = emptyList()) : Disposable {
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package org.fnives.test.showcase.android.testutil.synchronization.idlingresources
|
||||
|
||||
interface Disposable {
|
||||
val isDisposed: Boolean
|
||||
fun dispose()
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -1,12 +1,8 @@
|
|||
package org.fnives.test.showcase.testutils.idling
|
||||
package org.fnives.test.showcase.android.testutil.synchronization.idlingresources
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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.
|
||||
*
|
||||
* It is safe to call this from the MainThread.
|
||||
*/
|
||||
fun runOnUIAwaitOnCurrent(action: () -> Unit) {
|
||||
if (Looper.myLooper() === Looper.getMainLooper()) {
|
||||
action()
|
||||
} else {
|
||||
val deferred = CompletableDeferred<Unit>()
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
action()
|
||||
deferred.complete(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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -15,13 +15,3 @@ class LoopMainThreadFor(private val delayInMillis: Long) : ViewAction {
|
|||
uiController.loopMainThreadForAtLeast(delayInMillis)
|
||||
}
|
||||
}
|
||||
|
||||
class LoopMainThreadUntilIdle : ViewAction {
|
||||
override fun getConstraints(): Matcher<View> = Matchers.isA(View::class.java)
|
||||
|
||||
override fun getDescription(): String = "loop MainThread for until Idle"
|
||||
|
||||
override fun perform(uiController: UiController, view: View?) {
|
||||
uiController.loopMainThreadUntilIdle()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<View> = Matchers.isA(View::class.java)
|
||||
|
||||
override fun getDescription(): String = "loop MainThread for until Idle"
|
||||
|
||||
override fun perform(uiController: UiController, view: View?) {
|
||||
uiController.loopMainThreadUntilIdle()
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
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
|
||||
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
|
||||
1
test-util-junit5-android/.gitignore
vendored
Normal file
1
test-util-junit5-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
49
test-util-junit5-android/build.gradle
Normal file
49
test-util-junit5-android/build.gradle
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
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'
|
||||
}
|
||||
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 {
|
||||
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"
|
||||
}
|
||||
|
||||
ext.artifactId = "android-unit-junit5"
|
||||
apply from: "../gradlescripts/deploy.aar.gradle"
|
||||
0
test-util-junit5-android/consumer-rules.pro
Normal file
0
test-util-junit5-android/consumer-rules.pro
Normal file
21
test-util-junit5-android/proguard-rules.pro
vendored
Normal file
21
test-util-junit5-android/proguard-rules.pro
vendored
Normal 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
|
||||
5
test-util-junit5-android/src/main/AndroidManifest.xml
Normal file
5
test-util-junit5-android/src/main/AndroidManifest.xml
Normal 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>
|
||||
|
|
@ -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
|
||||
|
|
@ -11,9 +12,11 @@ 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")
|
||||
class InstantExecutorExtension : BeforeEachCallback, AfterEachCallback {
|
||||
|
||||
override fun beforeEach(context: ExtensionContext?) {
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
1
test-util-shared-android/.gitignore
vendored
Normal file
1
test-util-shared-android/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
47
test-util-shared-android/build.gradle
Normal file
47
test-util-shared-android/build.gradle
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
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'
|
||||
}
|
||||
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 {
|
||||
implementation "androidx.room:room-testing:$room_version"
|
||||
api project(':test-util-shared-robolectric')
|
||||
}
|
||||
|
||||
ext.artifactId = "shared-android"
|
||||
apply from: "../gradlescripts/deploy.aar.gradle"
|
||||
0
test-util-shared-android/consumer-rules.pro
Normal file
0
test-util-shared-android/consumer-rules.pro
Normal file
21
test-util-shared-android/proguard-rules.pro
vendored
Normal file
21
test-util-shared-android/proguard-rules.pro
vendored
Normal 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
|
||||
5
test-util-shared-android/src/main/AndroidManifest.xml
Normal file
5
test-util-shared-android/src/main/AndroidManifest.xml
Normal 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>
|
||||
|
|
@ -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 =
|
||||
1
test-util-shared-robolectric/.gitignore
vendored
Normal file
1
test-util-shared-robolectric/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
47
test-util-shared-robolectric/build.gradle
Normal file
47
test-util-shared-robolectric/build.gradle
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
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'
|
||||
}
|
||||
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 {
|
||||
implementation "androidx.room:room-testing:$room_version"
|
||||
implementation "androidx.arch.core:core-testing:$arch_core_version"
|
||||
}
|
||||
|
||||
ext.artifactId = "shared-robolectric"
|
||||
apply from: "../gradlescripts/deploy.aar.gradle"
|
||||
|
|
@ -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>
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("SwallowedException")
|
||||
private fun getAndroidClass() = try {
|
||||
Class.forName("org.fnives.test.showcase.android.testutil.AndroidMigrationTestRule")
|
||||
} catch (classNotFoundException: ClassNotFoundException) {
|
||||
null
|
||||
}
|
||||
|
|
@ -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
|
||||
Loading…
Add table
Add a link
Reference in a new issue