Skip to content

Commit

Permalink
Update FavoritesFragment UI tests
Browse files Browse the repository at this point in the history
There's still some issues in mocking the ViewModel/ViewModelactory

#29
  • Loading branch information
JoaquimLey committed Apr 15, 2018
1 parent aff5422 commit c511b3f
Show file tree
Hide file tree
Showing 26 changed files with 799 additions and 176 deletions.
54 changes: 15 additions & 39 deletions transport-eta-android/mobile-ui/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ android {
ignoreWarnings true
}

// Always show the result of every unit test, even if it passes.
testOptions.unitTests.all {
testLogging {
events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
}
}

buildTypes {
debug {
versionNameSuffix "-dev"
Expand All @@ -44,24 +51,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

// productFlavors {
// flavorDimensions "city", "environment"
//
// lx {
// dimension "city"
// }
//
// dev {
// applicationIdSuffix ".dev"
// dimension "environment"
// }
//
// mock {
// applicationIdSuffix ".mock"
// dimension "environment"
// }
// }
configurations.all {
resolutionStrategy {
force deps.kotlin.stdlib
force deps.support.annotations
}
}

kapt {
Expand All @@ -72,35 +68,24 @@ androidExtensions {
experimental = true
}

configurations.all {
resolutionStrategy {
force deps.kotlin.stdlib
}
}

dependencies {
// Modules
implementation project(':sms')

// Javax
implementation deps.javax.inject
compileOnly deps.javax.annotation

// Rx
implementation deps.rxjava2
implementation deps.rx_android

// Kotlin
implementation deps.kotlin.rx
implementation deps.kotlin.stdlib

// Dagger
implementation deps.dagger.runtime
implementation deps.dagger.android
implementation deps.dagger.android_support
kapt deps.dagger.compiler
kapt deps.dagger.android_support_compiler

// Support
implementation deps.support.v4
implementation deps.support.design
Expand All @@ -109,12 +94,10 @@ dependencies {
implementation deps.support.constraint
implementation deps.support.annotations
implementation deps.support.recyclerview

// Utils
implementation deps.glide
implementation deps.timber
implementation deps.lottie

// ACC
kapt deps.lifecycle.compiler
implementation deps.lifecycle.extensions
Expand All @@ -128,34 +111,27 @@ dependencies {
testImplementation deps.junit
testImplementation deps.hamcrest
testImplementation deps.kotlin.test
testImplementation deps.mockito.kotlin
testImplementation deps.mockito.inline
testImplementation deps.arch_core.testing

// Resolve conflicts between main and local unit tests
testImplementation deps.support.annotations
testImplementation deps.support.core_utils



// Dependencies for Android unit tests
// androidTestImplementation deps.dexmaker.mockito
androidTestImplementation deps.dexmaker.stdlib
androidTestImplementation deps.mockito.android
kaptAndroidTest deps.dagger.compiler

// Android Testing Support Library's runner and rules
androidTestImplementation deps.atsl.runner
androidTestImplementation deps.atsl.rules
androidTestImplementation deps.arch_core.testing

// Espresso UI Testing
androidTestImplementation deps.espresso.core
androidTestImplementation deps.espresso.contrib
androidTestImplementation deps.espresso.intents

// Resolve conflicts between main and test APK:
androidTestImplementation deps.support.annotations
androidTestImplementation deps.support.v4
androidTestImplementation deps.support.app_compat
androidTestImplementation deps.support.design

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ package com.joaquimley.transporteta.ui.di.component
import android.app.Application
import com.joaquimley.transporteta.sms.SmsBroadcastReceiver
import com.joaquimley.transporteta.sms.SmsController
import com.joaquimley.transporteta.ui.testing.di.module.TestActivityBindingModule
import com.joaquimley.transporteta.ui.di.module.TestActivityBindingModule
import com.joaquimley.transporteta.ui.di.module.TestAppModule
import com.joaquimley.transporteta.ui.injection.scope.PerApplication
import com.joaquimley.transporteta.ui.test.TestApplication
import dagger.BindsInstance
import dagger.Component
import dagger.android.support.AndroidSupportInjectionModule

@Component(modules = arrayOf(TestAppModule::class, TestActivityBindingModule::class,
@Component(modules = arrayOf(
TestAppModule::class,
TestActivityBindingModule::class,
AndroidSupportInjectionModule::class))
@PerApplication
interface TestAppComponent : AppComponent {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.joaquimley.transporteta.ui.testing.di.module
package com.joaquimley.transporteta.ui.di.module

import com.joaquimley.transporteta.ui.di.module.HomeFragmentsBuildersModule
import com.joaquimley.transporteta.ui.injection.scope.PerActivity
import com.joaquimley.transporteta.ui.testing.TestFragmentActivity
import dagger.Module
Expand All @@ -10,6 +9,6 @@ import dagger.android.ContributesAndroidInjector
abstract class TestActivityBindingModule {

@PerActivity
@ContributesAndroidInjector(modules = arrayOf(HomeFragmentsBuildersModule::class))
@ContributesAndroidInjector(modules = arrayOf(TestHomeFragmentsBuildersModule::class))
abstract fun bindTestFragmentActivity(): TestFragmentActivity
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.joaquimley.transporteta.ui.di.module

import com.joaquimley.transporteta.sms.SmsController
import com.joaquimley.transporteta.ui.test.FavoritesViewModelFactory
import dagger.Module
import dagger.Provides

@Module
class TestFavoriteFragmentModule {

@Provides
fun provideFavouritesViewModelFactory(smsController: SmsController): FavoritesViewModelFactory {
return FavoritesViewModelFactory(smsController)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.joaquimley.transporteta.ui.di.module

import com.joaquimley.transporteta.ui.home.favorite.FavoritesFragment
import dagger.Module
import dagger.android.ContributesAndroidInjector

@Module
abstract class TestHomeFragmentsBuildersModule {

@ContributesAndroidInjector(modules = arrayOf(TestFavoriteFragmentModule::class))
abstract fun contributeFavouritesFragment(): FavoritesFragment
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.joaquimley.transporteta.ui.home

import android.arch.core.executor.testing.InstantTaskExecutorRule
import android.arch.lifecycle.MutableLiveData
import android.support.test.espresso.Espresso.onView
import android.support.test.espresso.action.ViewActions.*
Expand All @@ -14,38 +15,30 @@ import com.joaquimley.transporteta.ui.home.favorite.FavoritesFragment
import com.joaquimley.transporteta.ui.home.favorite.FavoritesViewModel
import com.joaquimley.transporteta.ui.model.FavoriteView
import com.joaquimley.transporteta.ui.model.data.Resource
import com.joaquimley.transporteta.ui.test.util.RecyclerViewMatcher
import com.joaquimley.transporteta.ui.testing.TestFragmentActivity
import com.joaquimley.transporteta.ui.testing.factory.TestFactoryFavoriteView
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.mock

@MediumTest
@RunWith(AndroidJUnit4::class)
class FavoritesFragmentTest {

@Rule @JvmField val activityRule = ActivityTestRule(TestFragmentActivity::class.java)
@Rule @JvmField val activityRule = ActivityTestRule(TestFragmentActivity::class.java, true, true)
@Rule @JvmField val instantTaskExecutorRule = InstantTaskExecutorRule()

@Mock lateinit var viewModel: FavoritesViewModel
// lateinit var viewModel: FavoritesViewModel
private val results = MutableLiveData<Resource<List<FavoriteView>>>()
private val viewModel = mock(FavoritesViewModel::class.java)

private lateinit var favoritesFragment: FavoritesFragment
private val results = MutableLiveData<Resource<List<FavoriteView>>>()

@Before
fun setup() {
// Mock ViewModel
MockitoAnnotations.initMocks(this)
// viewModel = mock<FavoritesFragment>()
`when`(viewModel.getFavourites()).thenReturn(results)

// Init Activity&Fragment
favoritesFragment = FavoritesFragment.newInstance()
activityRule.activity.addFragment(favoritesFragment)
}
Expand Down Expand Up @@ -110,14 +103,20 @@ class FavoritesFragmentTest {


@Test
@Ignore("Test ignored: Not yet implemented")
fun whenDataComesInItIsCorrectlyDisplayedOnTheList() {
val resultsList = TestFactoryFavoriteView.generateFavoriteViewList()
results.postValue(Resource.success(resultsList))
onView(RecyclerViewMatcher.withRecyclerView(R.id.recycler_view).atPosition(0)).check(matches(hasDescendant(withText(resultsList[0].code.toString()))))
onView(withId(R.id.progress_bar)).check(matches(not(isDisplayed())))

// onView(RecyclerViewMatcher.withRecyclerView(R.id.recycler_view).atPosition(0))
// .check(matches(hasDescendant(withText(resultsList[0].latestEta))))
// onView(RecyclerViewMatcher.withRecyclerView(R.id.recycler_view).atPosition(0)).check(matches(hasDescendant(withText(resultsList[0].code.toString()))))
// onView(withId(R.id.progress_bar)).check(matches(not(isDisplayed())))
// onView(withText(resultsList[0].code.toString())).check(matches(isDisplayed()))
// viewModel.getFavourites()
}

// https://spin.atomicobject.com/2016/04/15/espresso-testing-recyclerviews/
// https://medium.com/@_rpiel/recyclerview-and-espresso-a-complicated-story-3f6f4179652e
}

}
// https://spin.atomicobject.com/2016/04/15/espresso-testing-recyclerviews/
// https://medium.com/@_rpiel/recyclerview-and-espresso-a-complicated-story-3f6f4179652e
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.joaquimley.transporteta.ui.test

import android.arch.lifecycle.ViewModel
import android.arch.lifecycle.ViewModelProvider
import com.joaquimley.transporteta.sms.SmsController


class FavoritesViewModelFactory(private val smsController: SmsController): ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(MockFavoritesViewModel::class.java)) {
return MockFavoritesViewModel(smsController) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.joaquimley.transporteta.ui.test

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.MutableLiveData
import com.joaquimley.transporteta.sms.SmsController
import com.joaquimley.transporteta.ui.home.favorite.FavoritesViewModel
import com.joaquimley.transporteta.ui.model.FavoriteView
import com.joaquimley.transporteta.ui.model.data.Resource
import com.joaquimley.transporteta.ui.testing.factory.TestFactoryFavoriteView

class MockFavoritesViewModel(smsController: SmsController): FavoritesViewModel(smsController) {

private val favouritesLiveData = MutableLiveData<Resource<List<FavoriteView>>>()

fun pushData(data: List<FavoriteView>) {
favouritesLiveData.postValue(Resource.success(data))
}

fun getCurrentdata(): Resource<List<FavoriteView>>? {
return favouritesLiveData.value
}

override fun getFavourites(): LiveData<Resource<List<FavoriteView>>> {
pushData(TestFactoryFavoriteView.generateFavoriteViewList())
return favouritesLiveData
}

override fun retry() {
// TODO make request to local database for favorites

}

override fun onEtaRequested(favourite: FavoriteView) {
requestEta(favourite.code)
}

private fun requestEta(code: Int) {

}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package com.joaquimley.transporteta.ui.test.util

import android.content.res.Resources
import android.support.test.InstrumentationRegistry
import android.support.v7.widget.RecyclerView
import android.view.View
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.TypeSafeMatcher
import org.hamcrest.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit


open class RecyclerViewMatcher constructor(var recyclerViewId: Int) {
Expand Down Expand Up @@ -66,6 +67,26 @@ open class RecyclerViewMatcher constructor(var recyclerViewId: Int) {
return RecyclerViewMatcher(recyclerViewId)
}

fun waitForAdapterChange(recyclerView: RecyclerView) {
val latch = CountDownLatch(1)
InstrumentationRegistry.getInstrumentation().runOnMainSync {
recyclerView.adapter.registerAdapterDataObserver(
object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
latch.countDown()
}

override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
latch.countDown()
}
})
}
if (recyclerView.adapter.itemCount > 0) {
return
}
MatcherAssert.assertThat(latch.await(10, TimeUnit.SECONDS), CoreMatchers.`is`(true))
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class TestFragmentActivity: AppCompatActivity(), HasSupportFragmentInjector {
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
val content = FrameLayout(this)
content.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
content.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
content.id = R.id.fragment_container
setContentView(content)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import dagger.Module
import dagger.Provides

@Module
class FavouriteFragmentModule {
class FavoriteFragmentModule {

@Provides
fun provideFavouritesViewModelFactory(smsController: SmsController): FavoritesViewModelFactory {
Expand Down
Loading

0 comments on commit c511b3f

Please sign in to comment.