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
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
testOptions {
unitTests.returnDefaultValues = true
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,14 @@
apply plugin: 'com.android.library'
apply from: '../core.gradle'
apply from: 'versions.gradle'
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:
active: true
EmptyFunctionBlock:
active: true
active: false
ignoreOverridden: false
EmptyIfBlock:
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()
}
}