diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..61ea717a01b6b6edf0a2844d3a60bffecd2f7bad --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/interoperability/InteroperabilityConfigurationFragmentViewModelTest.kt @@ -0,0 +1,70 @@ +package de.rki.coronawarnapp.ui.interoperability + +import androidx.lifecycle.MutableLiveData +import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.junit5.MockKExtension +import io.mockk.verify +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.extension.ExtendWith +import org.junit.jupiter.api.extension.Extensions +import testhelpers.extensions.InstantExecutorExtension +import testhelpers.extensions.getOrAwaitValue + +@Extensions( + ExtendWith(MockKExtension::class), + ExtendWith(InstantExecutorExtension::class) +) +class InteroperabilityConfigurationFragmentViewModelTest { + + @MockK(relaxUnitFun = true) + lateinit var interoperabilityRepository: InteroperabilityRepository + + private lateinit var interoperabilityConfigurationFragmentViewModel: InteroperabilityConfigurationFragmentViewModel + + private val countryListLiveData = MutableLiveData<List<String>>(listOf()) + + @BeforeEach + fun setupFreshViewModel() { + every { interoperabilityRepository.countryList } returns countryListLiveData + interoperabilityConfigurationFragmentViewModel = + InteroperabilityConfigurationFragmentViewModel(interoperabilityRepository) + } + + @Test + fun countryListIsEmptyIfRepositoryReturnsNoData() { + val countryList = + interoperabilityConfigurationFragmentViewModel.countryList.getOrAwaitValue() + + assertTrue(countryList.isEmpty()) + } + + @Test + fun testFetchCountryList() { + val countryListFetched = listOf( + "DE", "UK", "FR", "IT", "ES", "PL", "RO", "NL", + "BE", "CZ", "SE", "PT", "HU", "AT", "CH", "BG", "DK", "FI", "SK", + "NO", "IE", "HR", "SI", "LT", "LV", "EE", "CY", "LU", "MT", "IS" + ) + + interoperabilityRepository.getAllCountries() + countryListLiveData.value = countryListFetched + + val countryList = + interoperabilityConfigurationFragmentViewModel.countryList.getOrAwaitValue() + + assertEquals(countryList.size, countryListFetched.size) + assertTrue(countryList == countryListFetched) + verify { interoperabilityRepository.getAllCountries() } + } + + @Test + fun testBackPressButton() { + interoperabilityConfigurationFragmentViewModel.onBackPressed() + + assertTrue(interoperabilityConfigurationFragmentViewModel.navigateBack.getOrAwaitValue()) + } +} diff --git a/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt new file mode 100644 index 0000000000000000000000000000000000000000..1041e72dd00afe48bdd692be583af5696925023d --- /dev/null +++ b/Corona-Warn-App/src/test/java/testhelpers/extensions/LiveDataTestExtension.kt @@ -0,0 +1,58 @@ +package testhelpers.extensions + +/** + * Thanks to https://github.com/android/architecture-components-samples/blob/master/LiveDataSample/app/src/test/java/com/android/example/livedatabuilder/util/LiveDataTestUtil.kt + */ + +import androidx.lifecycle.LiveData +import androidx.lifecycle.Observer +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException + +/** + * Gets the value of a [LiveData] or waits for it to have one, with a timeout. + * + * Use this extension from host-side (JVM) tests. It's recommended to use it alongside + * `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously. + */ +fun <T> LiveData<T>.getOrAwaitValue( + time: Long = 2, + timeUnit: TimeUnit = TimeUnit.SECONDS, + afterObserve: () -> Unit = {} +): T { + var data: T? = null + val latch = CountDownLatch(1) + val observer = object : Observer<T> { + override fun onChanged(o: T?) { + data = o + latch.countDown() + this@getOrAwaitValue.removeObserver(this) + } + } + this.observeForever(observer) + + afterObserve.invoke() + + // Don't wait indefinitely if the LiveData is not set. + if (!latch.await(time, timeUnit)) { + this.removeObserver(observer) + throw TimeoutException("LiveData value was never set.") + } + + @Suppress("UNCHECKED_CAST") + return data as T +} + +/** + * Observes a [LiveData] until the `block` is done executing. + */ +fun <T> LiveData<T>.observeForTesting(block: () -> Unit) { + val observer = Observer<T> { } + try { + observeForever(observer) + block() + } finally { + removeObserver(observer) + } +}