Issue#106 Move SharedTest Folder into it's separate Android Module as suggested

This commit is contained in:
Gergely Hegedus 2022-09-26 17:25:50 +03:00
parent 9f79b4e67f
commit 9752d1643b
37 changed files with 172 additions and 232 deletions

1
app-shared-test/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,46 @@
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'
}
}
sourceSets {
main {
assets.srcDirs += files("$projectDir/../app/schemas".toString())
resources.srcDirs += files("$projectDir/../app/schemas".toString())
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation project(":app")
implementation project(':test-util-android')
implementation testFixtures(project(':core'))
implementation "io.insert-koin:koin-android:$koin_version"
implementation project(':test-util-shared-robolectric')
applyAppSharedTestDependenciesTo(this)
}

View file

21
app-shared-test/proguard-rules.pro vendored Normal file
View file

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View file

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

View file

@ -23,7 +23,7 @@ import java.io.IOException
* https://developer.android.com/training/data-storage/room/migrating-db-versions * https://developer.android.com/training/data-storage/room/migrating-db-versions
*/ */
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class MigrationToLatestInstrumentedTest { open class MigrationToLatestInstrumentedSharedTest {
@get:Rule @get:Rule
val helper = SharedMigrationTestRule<LocalDatabase>(instrumentation = InstrumentationRegistry.getInstrumentation()) val helper = SharedMigrationTestRule<LocalDatabase>(instrumentation = InstrumentationRegistry.getInstrumentation())
@ -48,7 +48,7 @@ class MigrationToLatestInstrumentedTest {
@Test @Test
@Throws(IOException::class) @Throws(IOException::class)
fun migrate1To2() { open fun migrate1To2() {
val expectedEntities = setOf( val expectedEntities = setOf(
FavouriteEntity("123"), FavouriteEntity("123"),
FavouriteEntity("124"), FavouriteEntity("124"),

View file

@ -26,7 +26,7 @@ import org.koin.test.KoinTest
@Suppress("TestFunctionName") @Suppress("TestFunctionName")
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class MainActivityInstrumentedTest : KoinTest { open class MainActivityInstrumentedSharedTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<MainActivity> private lateinit var activityScenario: ActivityScenario<MainActivity>

View file

@ -21,7 +21,7 @@ import org.koin.test.KoinTest
@Suppress("TestFunctionName") @Suppress("TestFunctionName")
@RunWith(AndroidJUnit4::class) @RunWith(AndroidJUnit4::class)
class AuthActivityInstrumentedTest : KoinTest { open class AuthActivityInstrumentedSharedTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<AuthActivity> private lateinit var activityScenario: ActivityScenario<AuthActivity>

View file

@ -3,7 +3,6 @@ package org.fnives.test.showcase.ui.splash
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.fnives.test.showcase.android.testutil.activity.SafeCloseActivityRule import org.fnives.test.showcase.android.testutil.activity.SafeCloseActivityRule
import org.fnives.test.showcase.android.testutil.intent.DismissSystemDialogsRule import org.fnives.test.showcase.android.testutil.intent.DismissSystemDialogsRule
import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule
@ -16,12 +15,10 @@ import org.junit.Before
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.RuleChain import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.koin.test.KoinTest import org.koin.test.KoinTest
@Suppress("TestFunctionName") @Suppress("TestFunctionName")
@RunWith(AndroidJUnit4::class) open class SplashActivityInstrumentedSharedTest : KoinTest {
class SplashActivityInstrumentedTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<SplashActivity> private lateinit var activityScenario: ActivityScenario<SplashActivity>

View file

@ -42,13 +42,11 @@ android {
sourceSets { sourceSets {
androidTest { androidTest {
java.srcDirs += "src/sharedTest/java" // assets.srcDirs += files("$projectDir/schemas".toString())
assets.srcDirs += files("$projectDir/schemas".toString())
} }
test { test {
java.srcDirs += "src/sharedTest/java"
java.srcDirs += "src/robolectricTest/java" java.srcDirs += "src/robolectricTest/java"
resources.srcDirs += files("$projectDir/schemas".toString()) // resources.srcDirs += files("$projectDir/schemas".toString())
} }
} }
@ -115,6 +113,9 @@ dependencies {
testImplementation testFixtures(project(':core')) testImplementation testFixtures(project(':core'))
androidTestImplementation testFixtures(project(':core')) androidTestImplementation testFixtures(project(':core'))
testImplementation project(':app-shared-test')
androidTestImplementation project(':app-shared-test')
// case specific // case specific
implementation project(":examplecase:example-navcontroller") implementation project(":examplecase:example-navcontroller")
} }

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.storage.migration
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MigrationToLatestInstrumentedTest : MigrationToLatestInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.home
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MainActivityInstrumentedTest : MainActivityInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.login
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AuthActivityInstrumentedTest : AuthActivityInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.splash
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SplashActivityInstrumentedTest : SplashActivityInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.storage.migration
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MigrationToLatestInstrumentedTest : MigrationToLatestInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.home
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MainActivityInstrumentedTest : MainActivityInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.login
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class AuthActivityInstrumentedTest : AuthActivityInstrumentedSharedTest()

View file

@ -0,0 +1,7 @@
package org.fnives.test.showcase.ui.splash
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class SplashActivityInstrumentedTest : SplashActivityInstrumentedSharedTest()

View file

@ -1,51 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.After
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.context.GlobalContext
import org.koin.test.KoinTest
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
@Ignore("CodeKata")
class CodeKataAuthActivitySharedTest : KoinTest {
@Before
fun setup() {
}
@After
fun tearDown() {
GlobalContext.stopKoin()
}
/** GIVEN non empty password and username and successful response WHEN signIn THEN no error is shown and navigating to home */
@Test
fun properLoginResultsInNavigationToHome() {
}
/** GIVEN empty password and username WHEN signIn THEN error password is shown */
@Test
fun emptyPasswordShowsProperErrorMessage() {
}
/** GIVEN password and empty username WHEN signIn THEN error username is shown */
@Test
fun emptyUserNameShowsProperErrorMessage() {
}
/** GIVEN password and username and invalid credentials response WHEN signIn THEN error invalid credentials is shown */
@Test
fun invalidCredentialsGivenShowsProperErrorMessage() {
}
/** GIVEN password and username and error response WHEN signIn THEN error invalid credentials is shown */
@Test
fun networkErrorShowsProperErrorMessage() {
}
}

View file

@ -1,69 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata
import androidx.annotation.StringRes
import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers
import org.fnives.test.showcase.R
import org.fnives.test.showcase.android.testutil.intent.notIntended
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsNotShown
import org.fnives.test.showcase.android.testutil.snackbar.SnackbarVerificationHelper.assertSnackBarIsShownWithText
import org.fnives.test.showcase.ui.home.MainActivity
import org.hamcrest.core.IsNot
class CodeKataSharedRobotTest {
fun setUsername(username: String): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.user_edit_text))
.perform(ViewActions.replaceText(username), ViewActions.closeSoftKeyboard())
}
fun setPassword(password: String): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.password_edit_text))
.perform(ViewActions.replaceText(password), ViewActions.closeSoftKeyboard())
}
fun clickOnLogin(): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.login_cta))
.perform(ViewActions.click())
}
fun assertPassword(password: String): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId((R.id.password_edit_text)))
.check(ViewAssertions.matches(ViewMatchers.withText(password)))
}
fun assertUsername(username: String): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId((R.id.user_edit_text)))
.check(ViewAssertions.matches(ViewMatchers.withText(username)))
}
fun assertLoadingBeforeRequests(): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.loading_indicator))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}
fun assertNotLoading(): CodeKataSharedRobotTest = apply {
Espresso.onView(ViewMatchers.withId(R.id.loading_indicator))
.check(ViewAssertions.matches(IsNot.not(ViewMatchers.isDisplayed())))
}
fun assertErrorIsShown(@StringRes stringResID: Int): CodeKataSharedRobotTest = apply {
assertSnackBarIsShownWithText(stringResID)
}
fun assertErrorIsNotShown(): CodeKataSharedRobotTest = apply {
assertSnackBarIsNotShown()
}
fun assertNavigatedToHome(): CodeKataSharedRobotTest = apply {
Intents.intended(IntentMatchers.hasComponent(MainActivity::class.java.canonicalName))
}
fun assertNotNavigatedToHome(): CodeKataSharedRobotTest = apply {
notIntended(IntentMatchers.hasComponent(MainActivity::class.java.canonicalName))
}
}

View file

@ -1,16 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata.rule.dispatcher
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
@OptIn(ExperimentalCoroutinesApi::class)
class CodeKataMainDispatcherRule : TestRule {
override fun apply(base: Statement, description: Description): Statement =
object : Statement() {
override fun evaluate() {
TODO("Not yet implemented")
}
}
}

View file

@ -1,41 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata.rule.dispatcher
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.storage.database.DatabaseInitialization
import org.fnives.test.showcase.testutils.storage.TestDatabaseInitialization
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
/**
* Sets up the Dispatcher as Main and as the [DatabaseInitialization]'s dispatcher.
*/
@OptIn(ExperimentalCoroutinesApi::class)
class PlainMainDispatcherRule(private val useStandard: Boolean = true) : TestRule {
private var _testDispatcher: TestDispatcher? = null
val testDispatcher
get() = _testDispatcher
?: throw IllegalStateException("TestDispatcher is accessed before it is initialized!")
override fun apply(base: Statement, description: Description): Statement = object : Statement() {
override fun evaluate() {
try {
val dispatcher = if (useStandard) StandardTestDispatcher() else UnconfinedTestDispatcher()
Dispatchers.setMain(dispatcher)
TestDatabaseInitialization.overwriteDatabaseInitialization(dispatcher)
_testDispatcher = dispatcher
base.evaluate()
} finally {
_testDispatcher = null
Dispatchers.resetMain()
}
}
}
}

View file

@ -1,11 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata.rule.intent
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
class CodeKataIntentInitRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
TODO()
}
}

View file

@ -1,25 +0,0 @@
package org.fnives.test.showcase.ui.login.codekata.rule.intent
import androidx.test.espresso.intent.Intents
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
/**
* Takes care of [Intents] initialization.
*/
class PlainIntentInitRule : TestRule {
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
try {
Intents.init()
base.evaluate()
} finally {
Intents.release()
}
}
}
}
}

View file

@ -21,6 +21,10 @@ project.ext {
applyAppTestDependenciesTo(this) applyAppTestDependenciesTo(this)
applyComposeTestDependenciesTo(this) // if you are using compose applyComposeTestDependenciesTo(this) // if you are using compose
} }
-------------APP-SHARED-TEST(Android Module-------------
dependencies {
applyAppSharedTestDependenciesTo(this)
}
------------------VERSIONS------------------ ------------------VERSIONS------------------
versions try to get the global value, if not found they fall back to some defaults. versions try to get the global value, if not found they fall back to some defaults.
@ -77,18 +81,24 @@ project.ext {
] ]
// ------------------PRIVATE------------------ // ------------------PRIVATE------------------
def applyStandardTestDependenciesTo = { module -> def standardTestDependencies = [
module.dependencies {
// coroutine testing // coroutine testing
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version" "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version",
// mockito, mocking library // mockito, mocking library
testImplementation "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version" "org.mockito.kotlin:mockito-kotlin:$testing_kotlin_mockito_version",
testImplementation "io.insert-koin:koin-test-junit5:$testing_koin_version" "io.insert-koin:koin-test-junit5:$testing_koin_version",
// junit5 // junit5
testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version",
testImplementation "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version" "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version",
]
def applyStandardTestDependenciesTo = { module ->
module.dependencies {
standardTestDependencies.forEach { dependency ->
testImplementation dependency
}
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
} }
} }
@ -137,6 +147,21 @@ project.ext {
} }
} }
// ------------APP-SHARED-TEST------------
applyAppSharedTestDependenciesTo = { module ->
module.dependencies {
standardTestDependencies.forEach { dependency ->
implementation dependency
}
androidSpecificTestDependencies.forEach { dependency ->
implementation dependency
}
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version"
implementation "io.insert-koin:koin-test-junit5:$testing_koin_version"
}
}
// ------------------COMPOSE------------------ // ------------------COMPOSE------------------
applyComposeTestDependenciesTo = { module -> applyComposeTestDependenciesTo = { module ->
module.dependencies { module.dependencies {

View file

@ -8,4 +8,5 @@ include ':test-util-shared-android'
include ':test-util-shared-robolectric' include ':test-util-shared-robolectric'
include ':test-util-android' include ':test-util-android'
include ':test-util-junit5-android' include ':test-util-junit5-android'
include ':app-shared-test'
include ':examplecase:example-navcontroller' include ':examplecase:example-navcontroller'