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
*/
@RunWith(AndroidJUnit4::class)
class MigrationToLatestInstrumentedTest {
open class MigrationToLatestInstrumentedSharedTest {
@get:Rule
val helper = SharedMigrationTestRule<LocalDatabase>(instrumentation = InstrumentationRegistry.getInstrumentation())
@ -48,7 +48,7 @@ class MigrationToLatestInstrumentedTest {
@Test
@Throws(IOException::class)
fun migrate1To2() {
open fun migrate1To2() {
val expectedEntities = setOf(
FavouriteEntity("123"),
FavouriteEntity("124"),

View file

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

View file

@ -21,7 +21,7 @@ import org.koin.test.KoinTest
@Suppress("TestFunctionName")
@RunWith(AndroidJUnit4::class)
class AuthActivityInstrumentedTest : KoinTest {
open class AuthActivityInstrumentedSharedTest : KoinTest {
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.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.SafeCloseActivityRule
import org.fnives.test.showcase.android.testutil.intent.DismissSystemDialogsRule
import org.fnives.test.showcase.android.testutil.screenshot.ScreenshotRule
@ -16,12 +15,10 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.RuleChain
import org.junit.runner.RunWith
import org.koin.test.KoinTest
@Suppress("TestFunctionName")
@RunWith(AndroidJUnit4::class)
class SplashActivityInstrumentedTest : KoinTest {
open class SplashActivityInstrumentedSharedTest : KoinTest {
private lateinit var activityScenario: ActivityScenario<SplashActivity>

View file

@ -42,13 +42,11 @@ android {
sourceSets {
androidTest {
java.srcDirs += "src/sharedTest/java"
assets.srcDirs += files("$projectDir/schemas".toString())
// assets.srcDirs += files("$projectDir/schemas".toString())
}
test {
java.srcDirs += "src/sharedTest/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'))
androidTestImplementation testFixtures(project(':core'))
testImplementation project(':app-shared-test')
androidTestImplementation project(':app-shared-test')
// case specific
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)
applyComposeTestDependenciesTo(this) // if you are using compose
}
-------------APP-SHARED-TEST(Android Module-------------
dependencies {
applyAppSharedTestDependenciesTo(this)
}
------------------VERSIONS------------------
versions try to get the global value, if not found they fall back to some defaults.
@ -77,18 +81,24 @@ project.ext {
]
// ------------------PRIVATE------------------
def applyStandardTestDependenciesTo = { module ->
module.dependencies {
def standardTestDependencies = [
// coroutine testing
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version"
"org.jetbrains.kotlinx:kotlinx-coroutines-test:$test_coroutines_version",
// 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
testImplementation "org.junit.jupiter:junit-jupiter-engine:$testing_junit5_version"
testImplementation "org.junit.jupiter:junit-jupiter-params:$testing_junit5_version"
"org.junit.jupiter:junit-jupiter-engine:$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"
}
}
@ -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------------------
applyComposeTestDependenciesTo = { module ->
module.dependencies {

View file

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