mirror of
https://github.com/imcarlost/Friendlists.git
synced 2026-04-10 02:46:54 -04:00
Merge pull request #9 from hakodeveloper/structure/testing
Create Tests
This commit is contained in:
@@ -3,4 +3,6 @@ apply from: '../core.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
@@ -37,6 +37,10 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
@@ -51,4 +55,6 @@ dependencies {
|
||||
implementation project(":userlist")
|
||||
implementation project(":albumlist")
|
||||
implementation project(":photolist")
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package com.hako.friendlists
|
||||
|
||||
import android.app.Application
|
||||
import com.hako.albumlist.di.albumListModules
|
||||
import com.hako.userlist.di.userlistModules
|
||||
import com.hako.friendlists.di.appModules
|
||||
import com.hako.photolist.di.photoListModules
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
import timber.log.Timber
|
||||
@@ -28,9 +26,7 @@ class MainApplication : Application() {
|
||||
modules(
|
||||
listOf(
|
||||
appModules,
|
||||
userlistModules,
|
||||
albumListModules,
|
||||
photoListModules
|
||||
userlistModules
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,19 +4,35 @@ import android.os.Bundle
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.hako.albumlist.di.albumListModules
|
||||
import com.hako.albumlist.feature.ALBUMLIST_FRAGMENT_BUNDLE_USER_ID
|
||||
import com.hako.albumlist.navigation.AlbumlistNavigation
|
||||
import com.hako.base.extensions.buildNavigation
|
||||
import com.hako.base.navigation.NavigationEvent
|
||||
import com.hako.friendlists.R
|
||||
import com.hako.photolist.di.photoListModules
|
||||
import com.hako.photolist.feature.PHOTOLIST_FRAGMENT_BUNDLE_ALBUM_ID
|
||||
import com.hako.userlist.navigation.UserlistNavigation
|
||||
import org.koin.core.context.loadKoinModules
|
||||
|
||||
// This sets the fragment title, it's referenced in every navigation
|
||||
const val FRAGMENT_TITLE = "actionTitle"
|
||||
|
||||
class NavigationViewmodel : ViewModel() {
|
||||
|
||||
// Load koin modules dynamically ;)
|
||||
private val albums by lazy {
|
||||
loadKoinModules(albumListModules)
|
||||
}
|
||||
|
||||
private val photos by lazy {
|
||||
loadKoinModules(photoListModules)
|
||||
}
|
||||
|
||||
private fun injectAlbums() = albums
|
||||
|
||||
private fun injectPhotos() = photos
|
||||
|
||||
val navigate = MutableLiveData<Pair<@IdRes Int, Bundle>>()
|
||||
|
||||
fun onNavigationEvent(event: NavigationEvent) {
|
||||
@@ -28,6 +44,8 @@ class NavigationViewmodel : ViewModel() {
|
||||
}
|
||||
|
||||
private fun handleUserlistNavigation(event: UserlistNavigation) {
|
||||
injectAlbums()
|
||||
|
||||
when (event) {
|
||||
is UserlistNavigation.ClickedOnUser -> navigate.postValue(
|
||||
buildNavigation(R.id.action_userlistFragment_to_albumlistFragment, Bundle().apply {
|
||||
@@ -42,6 +60,8 @@ class NavigationViewmodel : ViewModel() {
|
||||
}
|
||||
|
||||
private fun handleAlbumlistNavigation(event: AlbumlistNavigation) {
|
||||
injectPhotos()
|
||||
|
||||
when (event) {
|
||||
is AlbumlistNavigation.ClickedOnAlbum -> navigate.postValue(
|
||||
buildNavigation(R.id.action_albumlistFragment_to_photolistFragment, Bundle().apply {
|
||||
|
||||
@@ -52,12 +52,6 @@ dependencies {
|
||||
api deps.timber
|
||||
api deps.lottie
|
||||
api deps.picasso
|
||||
//Testing
|
||||
api deps.testing.junit
|
||||
api deps.testing.koin
|
||||
api deps.testing.core
|
||||
api deps.testing.rules
|
||||
api deps.testing.runner
|
||||
api deps.testing.ext
|
||||
api deps.testing.espresso
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
|
||||
@@ -15,9 +15,6 @@ interface AlbumDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveAll(entities: List<AlbumEntity>)
|
||||
|
||||
@get:Query("SELECT * FROM ${AlbumEntity.TABLE_NAME}")
|
||||
val all: List<AlbumEntity>
|
||||
|
||||
@Query("SELECT * FROM ${AlbumEntity.TABLE_NAME} WHERE userId = :userId ORDER BY id ASC")
|
||||
fun getAlbums(userId: Int): List<AlbumEntity>
|
||||
|
||||
|
||||
@@ -15,9 +15,6 @@ interface PhotoDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun saveAll(entities: List<PhotoEntity>)
|
||||
|
||||
@get:Query("SELECT * FROM ${PhotoEntity.TABLE_NAME}")
|
||||
val all: List<PhotoEntity>
|
||||
|
||||
@Query("SELECT * FROM ${PhotoEntity.TABLE_NAME} WHERE albumId = :albumId ORDER BY id ASC")
|
||||
fun getPhotos(albumId: Int): List<PhotoEntity>
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@ interface UserDao {
|
||||
@Query("UPDATE ${UserEntity.TABLE_NAME} SET isFavorite = :favorite WHERE id = :id")
|
||||
fun saveFavorite(id: Int, favorite: Boolean): Int
|
||||
|
||||
@get:Query("SELECT * FROM ${UserEntity.TABLE_NAME}")
|
||||
val all: List<UserEntity>
|
||||
|
||||
@Query("SELECT * FROM ${UserEntity.TABLE_NAME} ORDER BY id ASC")
|
||||
fun getAllUsers(): List<UserEntity>
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ data class UserEntity(
|
||||
val email: String,
|
||||
val phone: String,
|
||||
val website: String,
|
||||
val isFavorite: Boolean = true
|
||||
val isFavorite: Boolean = false
|
||||
) {
|
||||
companion object {
|
||||
const val TABLE_NAME = "users"
|
||||
|
||||
@@ -19,6 +19,10 @@ android {
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_1_8.toString()
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
|
||||
@@ -3,4 +3,6 @@ apply from: '../core.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
include ':app', ':base', ':userlist', ':albumlist', ':photolist'
|
||||
include ':app', ':base', ':userlist', ':albumlist', ':photolist', ':testing'
|
||||
rootProject.name='Friendlists'
|
||||
|
||||
1
testing/.gitignore
vendored
Normal file
1
testing/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
14
testing/build.gradle
Normal file
14
testing/build.gradle
Normal file
@@ -0,0 +1,14 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply from: '../core.gradle'
|
||||
apply from: 'versions.gradle'
|
||||
|
||||
dependencies {
|
||||
api deps.testing.junit
|
||||
api deps.testing.koin
|
||||
api deps.testing.core
|
||||
api deps.testing.rules
|
||||
api deps.testing.runner
|
||||
api deps.testing.ext
|
||||
api deps.testing.espresso
|
||||
api deps.testing.fragments
|
||||
}
|
||||
0
testing/consumer-rules.pro
Normal file
0
testing/consumer-rules.pro
Normal file
21
testing/proguard-rules.pro
vendored
Normal file
21
testing/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
|
||||
2
testing/src/main/AndroidManifest.xml
Normal file
2
testing/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.hako.testing" />
|
||||
10
testing/src/main/java/com/hako/testing/TestingExtensions.kt
Normal file
10
testing/src/main/java/com/hako/testing/TestingExtensions.kt
Normal file
@@ -0,0 +1,10 @@
|
||||
package com.hako.testing
|
||||
|
||||
import androidx.annotation.RestrictTo
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
|
||||
@RestrictTo(RestrictTo.Scope.TESTS)
|
||||
fun String.isTextDisplayed() = onView(withText(this)).check(matches(ViewMatchers.isDisplayed()))
|
||||
3
testing/src/main/res/values/strings.xml
Normal file
3
testing/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">testing</string>
|
||||
</resources>
|
||||
24
testing/versions.gradle
Normal file
24
testing/versions.gradle
Normal file
@@ -0,0 +1,24 @@
|
||||
ext.deps = [:]
|
||||
|
||||
def versions = [:]
|
||||
versions.koin = "2.0.1"
|
||||
versions.junit = "4.13"
|
||||
versions.test = "1.2.0"
|
||||
versions.test_ext = "1.1.1"
|
||||
versions.espresso = "3.2.0"
|
||||
versions.fragments = "1.2.0"
|
||||
|
||||
def deps = [:]
|
||||
|
||||
def testing = [:]
|
||||
testing.junit = "junit:junit:$versions.junit"
|
||||
testing.core = "androidx.test:core:$versions.test"
|
||||
testing.rules = "androidx.test:rules:$versions.test"
|
||||
testing.runner = "androidx.test:runner:$versions.test"
|
||||
testing.ext = "androidx.test.ext:junit:$versions.test_ext"
|
||||
testing.koin = "org.koin:koin-test:$versions.koin"
|
||||
testing.espresso = "androidx.test.espresso:espresso-core:$versions.espresso"
|
||||
testing.fragments = "androidx.fragment:fragment-testing:$versions.fragments"
|
||||
deps.testing = testing
|
||||
|
||||
ext.deps = deps
|
||||
@@ -108,7 +108,7 @@ empty-blocks:
|
||||
EmptyForBlock:
|
||||
active: true
|
||||
EmptyFunctionBlock:
|
||||
active: true
|
||||
active: false
|
||||
ignoreOverridden: false
|
||||
EmptyIfBlock:
|
||||
active: true
|
||||
|
||||
@@ -3,4 +3,6 @@ apply from: '../core.gradle'
|
||||
|
||||
dependencies {
|
||||
implementation project(':base')
|
||||
testImplementation project(':testing')
|
||||
androidTestImplementation project(':testing')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.hako.userlist.feature
|
||||
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.model.User
|
||||
import io.reactivex.Single
|
||||
|
||||
class MockUserDao(private val userList: List<UserEntity>) : UserDao {
|
||||
override fun save(entity: UserEntity) {}
|
||||
|
||||
override fun saveAll(entities: List<UserEntity>) {}
|
||||
|
||||
override fun saveFavorite(id: Int, favorite: Boolean) = 1
|
||||
|
||||
override fun getAllUsers() = userList
|
||||
|
||||
override fun getFavoriteUsers() = userList.filter { it.isFavorite }
|
||||
|
||||
override fun count() = userList.count()
|
||||
|
||||
override fun nukeDatabase() {}
|
||||
}
|
||||
|
||||
class MockUserApi(private val userList: List<User>) : UserlistRemoteApi {
|
||||
override fun getUsers() = Single.fromCallable { getAllUsers() }
|
||||
|
||||
private fun getAllUsers() = userList
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.hako.userlist.feature
|
||||
|
||||
import androidx.fragment.app.testing.launchFragmentInContainer
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.hako.base.domain.database.dao.UserDao
|
||||
import com.hako.base.domain.database.entities.UserEntity
|
||||
import com.hako.testing.isTextDisplayed
|
||||
import com.hako.userlist.domain.datasource.UserlistRemoteApi
|
||||
import com.hako.userlist.domain.usecase.GetFavoriteUsers
|
||||
import com.hako.userlist.domain.usecase.GetUsers
|
||||
import com.hako.userlist.domain.usecase.SetFavoriteStatus
|
||||
import com.hako.userlist.model.User
|
||||
import com.hako.userlist.model.toUserEntity
|
||||
import com.hako.userlist.viewmodel.UserlistViewmodel
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.koin.androidx.viewmodel.dsl.viewModel
|
||||
import org.koin.core.context.loadKoinModules
|
||||
import org.koin.core.context.startKoin
|
||||
import org.koin.core.context.stopKoin
|
||||
import org.koin.dsl.module
|
||||
|
||||
class UserlistFragmentTest {
|
||||
|
||||
@Before
|
||||
fun setupKoin() {
|
||||
startKoin {
|
||||
InstrumentationRegistry.getInstrumentation().targetContext
|
||||
modules(module {
|
||||
factory { GetUsers(get()) }
|
||||
factory { GetFavoriteUsers(get()) }
|
||||
factory { SetFavoriteStatus(get()) }
|
||||
viewModel { UserlistViewmodel() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun killKoin() {
|
||||
stopKoin()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldShowUserlist_withAllUsers() {
|
||||
userlist {
|
||||
withTwoBasicUsers()
|
||||
} should {
|
||||
showTwoBasicUsers()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shouldShowOnlyFavoriteUserlist_withAllUsers() {
|
||||
userlist {
|
||||
withTwoBasicOneFavoriteUsers()
|
||||
} should {
|
||||
showOnlyOneFavorite()
|
||||
}
|
||||
}
|
||||
|
||||
private fun userlist(func: UserlistRobot.() -> Unit) =
|
||||
UserlistRobot().apply {
|
||||
func()
|
||||
}
|
||||
}
|
||||
|
||||
class UserlistRobot {
|
||||
|
||||
fun withTwoBasicUsers() {
|
||||
loadKoinModules(
|
||||
module {
|
||||
factory<UserDao> { MockUserDao(loadTwoBasicUsers().map { it.toUserEntity() }) }
|
||||
factory<UserlistRemoteApi> { MockUserApi(loadTwoBasicUsers()) }
|
||||
}
|
||||
)
|
||||
launchFragmentInContainer<UserlistFragment>()
|
||||
}
|
||||
|
||||
fun withTwoBasicOneFavoriteUsers() {
|
||||
loadKoinModules(
|
||||
module {
|
||||
factory<UserDao> { MockUserDao(loadTwoUsersOneFavorite()) }
|
||||
factory<UserlistRemoteApi> { MockUserApi(loadTwoBasicUsers()) }
|
||||
}
|
||||
)
|
||||
launchFragmentInContainer<FavoriteUserlistFragment>()
|
||||
}
|
||||
|
||||
infix fun should(func: UserlistResult.() -> Unit) {
|
||||
UserlistResult().apply { func() }
|
||||
}
|
||||
|
||||
private fun loadTwoBasicUsers() = listOf(
|
||||
User(
|
||||
1,
|
||||
"Marian Arriaga",
|
||||
"mariancita",
|
||||
"test@gmail.com",
|
||||
"+56873912",
|
||||
"www.test.com"
|
||||
),
|
||||
User(
|
||||
2,
|
||||
"Carlos Martinez",
|
||||
"carlitos",
|
||||
"test2@gmail.com",
|
||||
"+56873912",
|
||||
"www.test2.com"
|
||||
)
|
||||
)
|
||||
|
||||
private fun loadTwoUsersOneFavorite() = listOf(
|
||||
UserEntity(
|
||||
1,
|
||||
"Marian Arriaga",
|
||||
"mariancita",
|
||||
"test@gmail.com",
|
||||
"+56873912",
|
||||
"www.test.com",
|
||||
true
|
||||
),
|
||||
UserEntity(
|
||||
2,
|
||||
"Carlos Martinez",
|
||||
"carlitos",
|
||||
"test2@gmail.com",
|
||||
"+56873912",
|
||||
"www.test2.com"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class UserlistResult {
|
||||
fun showTwoBasicUsers() {
|
||||
"Marian Arriaga".isTextDisplayed()
|
||||
"mariancita".isTextDisplayed()
|
||||
"Carlos Martinez".isTextDisplayed()
|
||||
"carlitos".isTextDisplayed()
|
||||
}
|
||||
|
||||
fun showOnlyOneFavorite() {
|
||||
"Marian Arriaga".isTextDisplayed()
|
||||
"mariancita".isTextDisplayed()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user