From 52891f670d88cd5dd81aa7637b931670da5d3bf7 Mon Sep 17 00:00:00 2001 From: Matthias Urhahn <matthias.urhahn@sap.com> Date: Thu, 4 Mar 2021 13:27:04 +0100 Subject: [PATCH] Small refactoring, a review of used scopes for coroutines (DEV) #2516 * Small refactoring, a review of used scopes for coroutines. Use AppScope instead of ViewModelScope, if there is a reasonable expectation that the a screen could be closed prematurely, and we still want an operation scoped to it's viewmodel to complete. * Fix tests Co-authored-by: ralfgehrer <mail@ralfgehrer.com> Co-authored-by: Lukas Lechner <lukas.lechner@sap.com> --- .../ContactDiaryEditLocationsFragmentTest.kt | 2 ++ .../ContactDiaryEditPersonsFragmentTest.kt | 2 ++ .../crash.ui/SettingsCrashReportViewModel.kt | 3 +-- ...iskLevelCalculationFragmentCWAViewModel.kt | 2 +- .../ContactDiaryEditLocationsViewModel.kt | 12 +++++++++--- .../edit/ContactDiaryEditPersonsViewModel.kt | 15 ++++++++++----- .../ContactDiaryAddLocationViewModel.kt | 18 ++++++++++++------ .../person/ContactDiaryAddPersonViewModel.kt | 16 +++++++++++----- .../util/viewmodel/CWAViewModel.kt | 9 +++++++-- .../ContactDiaryEditLocationsViewModelTest.kt | 19 +++++++++++++------ .../ContactDiaryEditPersonsViewModelTest.kt | 19 +++++++++++++------ 11 files changed, 81 insertions(+), 36 deletions(-) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt index 195824d42..7bb0ecd50 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditLocationsFragmentTest.kt @@ -12,6 +12,7 @@ import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.spyk +import kotlinx.coroutines.test.TestCoroutineScope import org.junit.After import org.junit.Before import org.junit.Rule @@ -44,6 +45,7 @@ class ContactDiaryEditLocationsFragmentTest : BaseUITest() { MockKAnnotations.init(this, relaxed = true) viewModel = spyk( ContactDiaryEditLocationsViewModel( + TestCoroutineScope(), contactDiaryRepository, TestDispatcherProvider() ) diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt index 1f0440d76..1e80c7199 100644 --- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt +++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/contactdiary/ContactDiaryEditPersonsFragmentTest.kt @@ -12,6 +12,7 @@ import io.mockk.MockKAnnotations import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.spyk +import kotlinx.coroutines.test.TestCoroutineScope import org.junit.After import org.junit.Before import org.junit.Rule @@ -44,6 +45,7 @@ class ContactDiaryEditPersonsFragmentTest : BaseUITest() { MockKAnnotations.init(this, relaxed = true) viewModel = spyk( ContactDiaryEditPersonsViewModel( + TestCoroutineScope(), contactDiaryRepository, TestDispatcherProvider() ) diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt index e9a0f09ea..b02e7a18b 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportViewModel.kt @@ -11,7 +11,6 @@ import de.rki.coronawarnapp.bugreporting.reportProblem import de.rki.coronawarnapp.bugreporting.storage.repository.BugRepository import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory -import kotlinx.coroutines.Dispatchers import timber.log.Timber class SettingsCrashReportViewModel @AssistedInject constructor( @@ -26,7 +25,7 @@ class SettingsCrashReportViewModel @AssistedInject constructor( createBugEventFormattedText(it) } - fun deleteAllCrashReports() = launch(Dispatchers.IO) { + fun deleteAllCrashReports() = launch { crashReportRepository.clear() } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt index 7066eebed..75beae1cd 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt @@ -202,7 +202,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor( fun shareExposureWindows() { Timber.d("Creating text file for Exposure Windows") - launch(dispatcherProvider.IO) { + launch { val exposureWindows = lastRiskResult.firstOrNull()?.exposureWindows?.map { it.toExposureWindowJson() } val fileNameCompatibleTimestamp = timeStamper.nowUTC.toString( DateTimeFormat.forPattern("yyyy-MM-DD-HH-mm-ss") diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt index d18e80939..89e11ab1e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModel.kt @@ -10,18 +10,24 @@ import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.map class ContactDiaryEditLocationsViewModel @AssistedInject constructor( + @AppScope private val appScope: CoroutineScope, private val contactDiaryRepository: ContactDiaryRepository, dispatcherProvider: DispatcherProvider ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - private val coroutineExceptionHandler = CoroutineExceptionHandler { _, ex -> - ex.report(ExceptionCategory.INTERNAL, TAG) + + init { + launchErrorHandler = CoroutineExceptionHandler { _, ex -> + ex.report(ExceptionCategory.INTERNAL, TAG) + } } val locationsLiveData = contactDiaryRepository.locations @@ -40,7 +46,7 @@ class ContactDiaryEditLocationsViewModel @AssistedInject constructor( } fun onDeleteAllConfirmedClick() { - launch(coroutineExceptionHandler) { + launch(scope = appScope) { contactDiaryRepository.deleteAllLocationVisits() contactDiaryRepository.deleteAllLocations() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt index d648222c2..2fe31146e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModel.kt @@ -10,17 +10,26 @@ import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.map class ContactDiaryEditPersonsViewModel @AssistedInject constructor( + @AppScope private val appScope: CoroutineScope, private val contactDiaryRepository: ContactDiaryRepository, dispatcherProvider: DispatcherProvider ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { + init { + launchErrorHandler = CoroutineExceptionHandler { _, ex -> + ex.report(ExceptionCategory.INTERNAL, TAG) + } + } + val navigationEvent = SingleLiveEvent<NavigationEvent>() val personsLiveData = contactDiaryRepository.people @@ -32,16 +41,12 @@ class ContactDiaryEditPersonsViewModel @AssistedInject constructor( val isListVisible = contactDiaryRepository.people.map { it.isNotEmpty() } .asLiveData(dispatcherProvider.IO) - private val coroutineExceptionHandler = CoroutineExceptionHandler { _, ex -> - ex.report(ExceptionCategory.INTERNAL, TAG) - } - fun onDeleteAllPersonsClick() { navigationEvent.postValue(NavigationEvent.ShowDeletionConfirmationDialog) } fun onDeleteAllConfirmedClick() { - launch(coroutineExceptionHandler) { + launch(scope = appScope) { contactDiaryRepository.deleteAllPersonEncounters() contactDiaryRepository.deleteAllPeople() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt index ca6484156..be3954c33 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/location/ContactDiaryAddLocationViewModel.kt @@ -11,23 +11,29 @@ import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import de.rki.coronawarnapp.ui.SingleLiveEvent +import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import org.joda.time.LocalDate class ContactDiaryAddLocationViewModel @AssistedInject constructor( + @AppScope private val appScope: CoroutineScope, dispatcherProvider: DispatcherProvider, @Assisted private val addedAt: String?, private val contactDiaryRepository: ContactDiaryRepository ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - private val coroutineExceptionHandler = CoroutineExceptionHandler { _, ex -> - shouldClose.postValue(null) - ex.report(ExceptionCategory.INTERNAL, TAG) + + init { + launchErrorHandler = CoroutineExceptionHandler { _, ex -> + shouldClose.postValue(null) + ex.report(ExceptionCategory.INTERNAL, TAG) + } } val shouldClose = SingleLiveEvent<Unit>() @@ -42,7 +48,7 @@ class ContactDiaryAddLocationViewModel @AssistedInject constructor( locationName.value = value } - fun addLocation(phoneNumber: String, emailAddress: String) = launch(coroutineExceptionHandler) { + fun addLocation(phoneNumber: String, emailAddress: String) = launch(scope = appScope) { val location = contactDiaryRepository.addLocation( DefaultContactDiaryLocation( locationName = locationName.value, @@ -63,7 +69,7 @@ class ContactDiaryAddLocationViewModel @AssistedInject constructor( } fun updateLocation(location: ContactDiaryLocationEntity, phoneNumber: String, emailAddress: String) = - launch(coroutineExceptionHandler) { + launch(scope = appScope) { contactDiaryRepository.updateLocation( DefaultContactDiaryLocation( location.locationId, @@ -75,7 +81,7 @@ class ContactDiaryAddLocationViewModel @AssistedInject constructor( shouldClose.postValue(null) } - fun deleteLocation(location: ContactDiaryLocationEntity) = launch(coroutineExceptionHandler) { + fun deleteLocation(location: ContactDiaryLocationEntity) = launch(scope = appScope) { contactDiaryRepository.locationVisits.firstOrNull()?.forEach { if (it.contactDiaryLocation.locationId == location.locationId) contactDiaryRepository.deleteLocationVisit(it) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt index 34eb68336..6ef9c0ad6 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/person/ContactDiaryAddPersonViewModel.kt @@ -10,23 +10,29 @@ import de.rki.coronawarnapp.contactdiary.storage.entity.ContactDiaryPersonEntity import de.rki.coronawarnapp.contactdiary.storage.repo.ContactDiaryRepository import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report +import de.rki.coronawarnapp.util.coroutine.AppScope import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import de.rki.coronawarnapp.util.ui.SingleLiveEvent import de.rki.coronawarnapp.util.viewmodel.CWAViewModel import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map import org.joda.time.LocalDate class ContactDiaryAddPersonViewModel @AssistedInject constructor( + @AppScope private val appScope: CoroutineScope, dispatcherProvider: DispatcherProvider, @Assisted private val addedAt: String?, private val contactDiaryRepository: ContactDiaryRepository ) : CWAViewModel(dispatcherProvider = dispatcherProvider) { - private val coroutineExceptionHandler = CoroutineExceptionHandler { _, ex -> - ex.report(ExceptionCategory.INTERNAL, TAG) + + init { + launchErrorHandler = CoroutineExceptionHandler { _, ex -> + ex.report(ExceptionCategory.INTERNAL, TAG) + } } val shouldClose = SingleLiveEvent<Unit>() @@ -41,7 +47,7 @@ class ContactDiaryAddPersonViewModel @AssistedInject constructor( name.value = value } - fun addPerson(phoneNumber: String, emailAddress: String) = launch(coroutineExceptionHandler) { + fun addPerson(phoneNumber: String, emailAddress: String) = launch(scope = appScope) { val person = contactDiaryRepository.addPerson( DefaultContactDiaryPerson( fullName = name.value, @@ -62,7 +68,7 @@ class ContactDiaryAddPersonViewModel @AssistedInject constructor( } fun updatePerson(person: ContactDiaryPersonEntity, phoneNumber: String, emailAddress: String) = - launch(coroutineExceptionHandler) { + launch(scope = appScope) { contactDiaryRepository.updatePerson( DefaultContactDiaryPerson( person.personId, @@ -75,7 +81,7 @@ class ContactDiaryAddPersonViewModel @AssistedInject constructor( shouldClose.postValue(null) } - fun deletePerson(person: ContactDiaryPersonEntity) = launch(coroutineExceptionHandler) { + fun deletePerson(person: ContactDiaryPersonEntity) = launch(scope = appScope) { contactDiaryRepository.personEncounters.firstOrNull()?.forEach { if (it.contactDiaryPerson.personId == person.personId) contactDiaryRepository.deletePersonEncounter(it) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt index 32217bfaf..eca763ca8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/viewmodel/CWAViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope import de.rki.coronawarnapp.util.coroutine.DefaultDispatcherProvider import de.rki.coronawarnapp.util.coroutine.DispatcherProvider import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.launchIn @@ -20,6 +21,7 @@ abstract class CWAViewModel constructor( ) : ViewModel() { private val tag: String = this::class.simpleName!! + var launchErrorHandler: CoroutineExceptionHandler? = null init { Timber.tag(tag).v("Initialized") @@ -30,13 +32,16 @@ abstract class CWAViewModel constructor( * Remember to switch to the main thread if you want to update the UI directly */ fun launch( + scope: CoroutineScope = viewModelScope, context: CoroutineContext = dispatcherProvider.Default, + errorHandler: CoroutineExceptionHandler? = launchErrorHandler, block: suspend CoroutineScope.() -> Unit ) { + val combinedContext = errorHandler?.let { context + it } ?: context try { - viewModelScope.launch(context = context, block = block) + scope.launch(context = combinedContext, block = block) } catch (e: CancellationException) { - Timber.w(e, "launch()ed coroutine was canceled.") + Timber.w(e, "launch()ed coroutine was canceled (scope=%s).", scope) } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt index 6dd141a33..85cec1b37 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditLocationsViewModelTest.kt @@ -12,6 +12,7 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestCoroutineScope import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -37,10 +38,16 @@ class ContactDiaryEditLocationsViewModelTest { MockKAnnotations.init(this) } + fun createInstance() = ContactDiaryEditLocationsViewModel( + appScope = TestCoroutineScope(), + contactDiaryRepository = contactDiaryRepository, + dispatcherProvider = TestDispatcherProvider() + ) + @Test fun testOnDeleteAllLocationsClick() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.navigationEvent.observeForever { } viewModel.onDeleteAllLocationsClick() viewModel.navigationEvent.value shouldBe ContactDiaryEditLocationsViewModel.NavigationEvent.ShowDeletionConfirmationDialog @@ -51,7 +58,7 @@ class ContactDiaryEditLocationsViewModelTest { coEvery { contactDiaryRepository.deleteAllLocationVisits() } just Runs coEvery { contactDiaryRepository.deleteAllLocations() } just Runs every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.onDeleteAllConfirmedClick() coVerify(exactly = 1) { contactDiaryRepository.deleteAllLocationVisits() @@ -62,7 +69,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testOnEditLocationClick() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.navigationEvent.observeForever { } viewModel.onEditLocationClick(location) viewModel.navigationEvent.value shouldBe @@ -72,7 +79,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testIsButtonEnabled() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe true } @@ -80,7 +87,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testIsButtonNotEnabledWhenListIsEmpty() { every { contactDiaryRepository.locations } returns MutableStateFlow(emptyList()) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe false } @@ -88,7 +95,7 @@ class ContactDiaryEditLocationsViewModelTest { @Test fun testLocations() { every { contactDiaryRepository.locations } returns MutableStateFlow(locationList) - viewModel = ContactDiaryEditLocationsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.locationsLiveData.observeForever { } viewModel.locationsLiveData.value shouldBe locationList } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt index 892d74d8b..babe9784b 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/edit/ContactDiaryEditPersonsViewModelTest.kt @@ -12,6 +12,7 @@ import io.mockk.every import io.mockk.impl.annotations.MockK import io.mockk.just import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestCoroutineScope import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @@ -38,10 +39,16 @@ class ContactDiaryEditPersonsViewModelTest { MockKAnnotations.init(this) } + private fun createInstance() = ContactDiaryEditPersonsViewModel( + appScope = TestCoroutineScope(), + contactDiaryRepository = contactDiaryRepository, + dispatcherProvider = TestDispatcherProvider() + ) + @Test fun testOnDeleteAllLocationsClick() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.navigationEvent.observeForever { } viewModel.onDeleteAllPersonsClick() viewModel.navigationEvent.value shouldBe ContactDiaryEditPersonsViewModel.NavigationEvent.ShowDeletionConfirmationDialog @@ -52,7 +59,7 @@ class ContactDiaryEditPersonsViewModelTest { coEvery { contactDiaryRepository.deleteAllPeople() } just Runs coEvery { contactDiaryRepository.deleteAllPersonEncounters() } just Runs every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.onDeleteAllConfirmedClick() coVerify(exactly = 1) { contactDiaryRepository.deleteAllPeople() @@ -63,7 +70,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testOnEditLocationClick() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.navigationEvent.observeForever { } viewModel.onEditPersonClick(person) viewModel.navigationEvent.value shouldBe @@ -73,7 +80,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testIsButtonEnabled() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe true } @@ -81,7 +88,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testIsButtonNotEnabledWhenListIsEmpty() { every { contactDiaryRepository.people } returns MutableStateFlow(emptyList()) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.isButtonEnabled.observeForever { } viewModel.isButtonEnabled.value shouldBe false } @@ -89,7 +96,7 @@ class ContactDiaryEditPersonsViewModelTest { @Test fun testLocations() { every { contactDiaryRepository.people } returns MutableStateFlow(personList) - viewModel = ContactDiaryEditPersonsViewModel(contactDiaryRepository, TestDispatcherProvider()) + viewModel = createInstance() viewModel.personsLiveData.observeForever { } viewModel.personsLiveData.value shouldBe personList } -- GitLab