Skip to content

Commit

Permalink
Adding DatePickerViewModelTest
Browse files Browse the repository at this point in the history
  • Loading branch information
philipplackner committed Aug 8, 2023
1 parent c25605b commit 14a3341
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package com.ivy.common.androidtest
import android.content.Context
import androidx.datastore.preferences.core.edit
import androidx.test.core.app.ApplicationProvider
import com.ivy.common.time.provider.TimeProvider
import com.ivy.core.persistence.IvyWalletCoreDb
import com.ivy.core.persistence.datastore.dataStore
import dagger.hilt.android.testing.HiltAndroidRule
import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before
import org.junit.Rule
import java.time.LocalDate
import javax.inject.Inject

abstract class IvyAndroidTest {
Expand All @@ -20,6 +22,9 @@ abstract class IvyAndroidTest {
@Inject
lateinit var db: IvyWalletCoreDb

@Inject
lateinit var timeProvider: TimeProvider

protected lateinit var context: Context

@Before
Expand All @@ -35,6 +40,13 @@ abstract class IvyAndroidTest {
db.close()
}

protected fun setDate(date: LocalDate) {
(timeProvider as TimeProviderFake).apply {
timeNow = date.atTime(12, 0)
dateNow = date
}
}

private fun clearDataStore() = runBlocking {
context.dataStore.edit {
it.clear()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.ivy.common.androidtest

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
import org.junit.rules.TestWatcher
import org.junit.runner.Description

@OptIn(ExperimentalCoroutinesApi::class)
class MainCoroutineRule(
val testDispatcher: TestDispatcher = StandardTestDispatcher()
) : TestWatcher() {

override fun starting(description: Description?) {
Dispatchers.setMain(testDispatcher)
}

override fun finished(description: Description?) {
Dispatchers.resetMain()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.ivy.common.androidtest

import com.ivy.common.di.CommonModuleDI
import com.ivy.common.time.provider.TimeProvider
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dagger.hilt.testing.TestInstallIn
import javax.inject.Singleton

@Module
@TestInstallIn(
components = [SingletonComponent::class],
replaces = [CommonModuleDI::class]
)
abstract class TestCommonModuleDI {
@Binds
abstract fun timeProvider(provider: TimeProviderFake): TimeProvider

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.ivy.common.androidtest

import com.ivy.common.time.provider.TimeProvider
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import javax.inject.Inject
import javax.inject.Singleton

@Singleton
class TimeProviderFake @Inject constructor(): TimeProvider {

var timeNow = LocalDateTime.now()
var dateNow = LocalDate.now()
var zoneId = ZoneId.systemDefault()

override fun timeNow(): LocalDateTime {
return timeNow
}

override fun dateNow(): LocalDate {
return dateNow
}

override fun zoneId(): ZoneId {
return zoneId
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ abstract class FlowViewModel<InternalState, UiState, Event> : ViewModel() {

protected val state: StateFlow<InternalState> by lazy {
stateFlow
.flowOn(Dispatchers.Default)
.flowOn(Dispatchers.Main)
.onEach {
Timber.d("Internal state = $it")
}
Expand All @@ -35,7 +35,7 @@ abstract class FlowViewModel<InternalState, UiState, Event> : ViewModel() {
val uiState: StateFlow<UiState> by lazy {
uiFlow.onEach {
Timber.d("UI state = $it")
}.flowOn(Dispatchers.Default)
}.flowOn(Dispatchers.Main)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5_000L),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.ivy.core.ui.time.picker.date

import app.cash.turbine.test
import assertk.assertThat
import assertk.assertions.isEqualTo
import com.ivy.common.androidtest.IvyAndroidTest
import com.ivy.common.androidtest.MainCoroutineRule
import com.ivy.core.ui.time.picker.date.data.PickerDay
import com.ivy.core.ui.time.picker.date.data.PickerMonth
import dagger.hilt.android.testing.HiltAndroidTest
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
import java.time.LocalDate

@OptIn(ExperimentalCoroutinesApi::class)
@HiltAndroidTest
class DatePickerViewModelTest: IvyAndroidTest() {

@get:Rule
val mainCoroutineRule = MainCoroutineRule()

private lateinit var viewModel: DatePickerViewModel

override fun setUp() {
super.setUp()
viewModel = DatePickerViewModel(
appContext = context,
timeProvider = timeProvider
)
}

@Test
fun testSelectingDate() = runTest {
// Making sure, the test runs with a month != February, so
// February can be selected
setDate(LocalDate.of(2023, 1, 1))
viewModel.uiState.test {
awaitItem() // Skip initial emission

viewModel.onEvent(DatePickerEvent.DayChange(PickerDay("30", 30)))
awaitItem() // Skip day emission

viewModel.onEvent(DatePickerEvent.MonthChange(PickerMonth("Feb", 2)))

val finalEmission = awaitItem()

val timeProviderDate = timeProvider.dateNow()
assertThat(finalEmission.selected).isEqualTo(
LocalDate.of(timeProviderDate.year, 2, 28)
)
}
}
}

0 comments on commit 14a3341

Please sign in to comment.