implement testing and create a simple test for Userlist

This commit is contained in:
Carlos Martinez
2020-02-06 14:34:42 -03:00
parent 40e57a27ac
commit b720fb39e6
15 changed files with 199 additions and 25 deletions

View File

@@ -37,6 +37,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
testOptions { testOptions {
unitTests.returnDefaultValues = true unitTests.returnDefaultValues = true
} }

View File

@@ -1,10 +1,8 @@
package com.hako.friendlists package com.hako.friendlists
import android.app.Application import android.app.Application
import com.hako.albumlist.di.albumListModules
import com.hako.userlist.di.userlistModules import com.hako.userlist.di.userlistModules
import com.hako.friendlists.di.appModules import com.hako.friendlists.di.appModules
import com.hako.photolist.di.photoListModules
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import timber.log.Timber import timber.log.Timber
@@ -28,9 +26,7 @@ class MainApplication : Application() {
modules( modules(
listOf( listOf(
appModules, appModules,
userlistModules, userlistModules
albumListModules,
photoListModules
) )
) )
} }

View File

@@ -4,19 +4,35 @@ import android.os.Bundle
import androidx.annotation.IdRes import androidx.annotation.IdRes
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.hako.albumlist.di.albumListModules
import com.hako.albumlist.feature.ALBUMLIST_FRAGMENT_BUNDLE_USER_ID import com.hako.albumlist.feature.ALBUMLIST_FRAGMENT_BUNDLE_USER_ID
import com.hako.albumlist.navigation.AlbumlistNavigation import com.hako.albumlist.navigation.AlbumlistNavigation
import com.hako.base.extensions.buildNavigation import com.hako.base.extensions.buildNavigation
import com.hako.base.navigation.NavigationEvent import com.hako.base.navigation.NavigationEvent
import com.hako.friendlists.R import com.hako.friendlists.R
import com.hako.photolist.di.photoListModules
import com.hako.photolist.feature.PHOTOLIST_FRAGMENT_BUNDLE_ALBUM_ID import com.hako.photolist.feature.PHOTOLIST_FRAGMENT_BUNDLE_ALBUM_ID
import com.hako.userlist.navigation.UserlistNavigation import com.hako.userlist.navigation.UserlistNavigation
import org.koin.core.context.loadKoinModules
// This sets the fragment title, it's referenced in every navigation // This sets the fragment title, it's referenced in every navigation
const val FRAGMENT_TITLE = "actionTitle" const val FRAGMENT_TITLE = "actionTitle"
class NavigationViewmodel : ViewModel() { 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>>() val navigate = MutableLiveData<Pair<@IdRes Int, Bundle>>()
fun onNavigationEvent(event: NavigationEvent) { fun onNavigationEvent(event: NavigationEvent) {
@@ -28,6 +44,8 @@ class NavigationViewmodel : ViewModel() {
} }
private fun handleUserlistNavigation(event: UserlistNavigation) { private fun handleUserlistNavigation(event: UserlistNavigation) {
injectAlbums()
when (event) { when (event) {
is UserlistNavigation.ClickedOnUser -> navigate.postValue( is UserlistNavigation.ClickedOnUser -> navigate.postValue(
buildNavigation(R.id.action_userlistFragment_to_albumlistFragment, Bundle().apply { buildNavigation(R.id.action_userlistFragment_to_albumlistFragment, Bundle().apply {
@@ -42,6 +60,8 @@ class NavigationViewmodel : ViewModel() {
} }
private fun handleAlbumlistNavigation(event: AlbumlistNavigation) { private fun handleAlbumlistNavigation(event: AlbumlistNavigation) {
injectPhotos()
when (event) { when (event) {
is AlbumlistNavigation.ClickedOnAlbum -> navigate.postValue( is AlbumlistNavigation.ClickedOnAlbum -> navigate.postValue(
buildNavigation(R.id.action_albumlistFragment_to_photolistFragment, Bundle().apply { buildNavigation(R.id.action_albumlistFragment_to_photolistFragment, Bundle().apply {

View File

@@ -52,12 +52,6 @@ dependencies {
api deps.timber api deps.timber
api deps.lottie api deps.lottie
api deps.picasso api deps.picasso
//Testing testImplementation project(':testing')
api deps.testing.junit androidTestImplementation project(':testing')
api deps.testing.koin
api deps.testing.core
api deps.testing.rules
api deps.testing.runner
api deps.testing.ext
api deps.testing.espresso
} }

View File

@@ -15,9 +15,6 @@ interface AlbumDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveAll(entities: List<AlbumEntity>) 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") @Query("SELECT * FROM ${AlbumEntity.TABLE_NAME} WHERE userId = :userId ORDER BY id ASC")
fun getAlbums(userId: Int): List<AlbumEntity> fun getAlbums(userId: Int): List<AlbumEntity>

View File

@@ -15,9 +15,6 @@ interface PhotoDao {
@Insert(onConflict = OnConflictStrategy.REPLACE) @Insert(onConflict = OnConflictStrategy.REPLACE)
fun saveAll(entities: List<PhotoEntity>) 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") @Query("SELECT * FROM ${PhotoEntity.TABLE_NAME} WHERE albumId = :albumId ORDER BY id ASC")
fun getPhotos(albumId: Int): List<PhotoEntity> fun getPhotos(albumId: Int): List<PhotoEntity>

View File

@@ -18,9 +18,6 @@ interface UserDao {
@Query("UPDATE ${UserEntity.TABLE_NAME} SET isFavorite = :favorite WHERE id = :id") @Query("UPDATE ${UserEntity.TABLE_NAME} SET isFavorite = :favorite WHERE id = :id")
fun saveFavorite(id: Int, favorite: Boolean): Int 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") @Query("SELECT * FROM ${UserEntity.TABLE_NAME} ORDER BY id ASC")
fun getAllUsers(): List<UserEntity> fun getAllUsers(): List<UserEntity>

View File

@@ -13,7 +13,7 @@ data class UserEntity(
val email: String, val email: String,
val phone: String, val phone: String,
val website: String, val website: String,
val isFavorite: Boolean = true val isFavorite: Boolean = false
) { ) {
companion object { companion object {
const val TABLE_NAME = "users" const val TABLE_NAME = "users"

View File

@@ -19,6 +19,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
testOptions { testOptions {
unitTests.returnDefaultValues = true unitTests.returnDefaultValues = true
} }

View File

@@ -1,6 +1,14 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply from: '../core.gradle' apply from: '../core.gradle'
apply from: 'versions.gradle'
dependencies { dependencies {
implementation project(':base') 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
} }

View 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()))

24
testing/versions.gradle Normal file
View 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

View File

@@ -108,7 +108,7 @@ empty-blocks:
EmptyForBlock: EmptyForBlock:
active: true active: true
EmptyFunctionBlock: EmptyFunctionBlock:
active: true active: false
ignoreOverridden: false ignoreOverridden: false
EmptyIfBlock: EmptyIfBlock:
active: true active: true

View File

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

View File

@@ -0,0 +1,94 @@
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.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.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.dsl.module
class UserlistFragmentTest {
@Before
fun setupKoin() {
startKoin {
InstrumentationRegistry.getInstrumentation().targetContext
modules(module {
factory { GetUsers(get()) }
factory { GetFavoriteUsers(get()) }
factory { SetFavoriteStatus(get()) }
viewModel { UserlistViewmodel() }
})
}
}
@Test
fun shouldShowUserlist_withAllUsers() {
userlist {
initialState()
} should {
showTwoBasicUsers()
}
}
private fun userlist(func: UserlistRobot.() -> Unit) =
UserlistRobot().apply {
func()
}
}
class UserlistRobot {
fun initialState() {
loadKoinModules(
module {
factory<UserDao> { MockUserDao(loadTwoBasicUsers().map { it.toUserEntity() }) }
factory<UserlistRemoteApi> { MockUserApi(loadTwoBasicUsers()) }
}
)
launchFragmentInContainer<UserlistFragment>()
}
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"
)
)
}
class UserlistResult {
fun showTwoBasicUsers() {
"Marian Arriaga".isTextDisplayed()
"mariancita".isTextDisplayed()
"Carlos Martinez".isTextDisplayed()
"carlitos".isTextDisplayed()
}
}