when i try to call frankfurterApi.convert with test scope, the test instantly gets terminated(println("here2") doesn't get called), but with runBlocking everything works good, just like with viewModelScope in production
test:
private lateinit var mockWebServer: MockWebServer
@Before
fun init() {
mockWebServer = MockWebServer()
auth = mockAuth()
firestore = mockFirestore(listener)
}
@Test
fun requestConversion_success() = runTest {
val currentBalance = 234f
val from = CurrencyEnum.EUR
val to = CurrencyEnum.USD
val api = Retrofit.Builder().baseUrl(mockWebServer.url("/"))
.addConverterFactory(GsonConverterFactory.create())
.build().create(FrankfurterApi::class.java)
val jsonResponse = """
{
"amount": $currentBalance,
"base": "$from",
"date": "2024-06-12",
"rates": {
"$to": 219.3
}
}
"""
mockWebServer.enqueue(MockResponse().setBody(jsonResponse).setResponseCode(200))
val datastoreManager = mockk<DataStoreManager> {
every { balanceFlow() } returns flowOf(Pair(from.ordinal, currentBalance))
every { themeFlow() } returns flowOf()
}
val repository = SettingsRepository(auth, firestore.collection("data"), api, datastoreManager)
val viewModel = SettingsViewModel(repository, CoroutineScopeProvider(this))
advanceUntilIdle()
viewModel.changeCurrency(to)
advanceUntilIdle()
println(viewModel.uiState.value.currencyChangeResult)
assertTrue(viewModel.uiState.value.currencyChangeResult is Result.Success)
}
view model:
@HiltViewModel
class SettingsViewModel @Inject constructor(
private val settingsRepository: SettingsRepository,
scopeProvider: CoroutineScopeProvider
): ViewModel() {
private val _uiState = MutableStateFlow(UiState())
private val scope = scopeProvider.provide() ?: viewModelScope
val uiState = _uiState.asStateFlow()
fun changeCurrency(newCurrency: CurrencyEnum) = scope.launch {
val balance = _uiState.value.balance
settingsRepository.changeCurrency(balance, newCurrency)
.catch { updateCurrencyChangeResult(Result.Error(it.message ?: it.toString())) }
.collect { updateCurrencyChangeResult(it) }
}
scope provider:
class CoroutineScopeProvider(private val coroutineScope: CoroutineScope? = null) {
fun provide() = coroutineScope
}
repository:
class SettingsRepository(
private val auth: FirebaseAuth,
private val firestore: CollectionReference,
private val frankfurterApi: FrankfurterApi,
private val dataStoreManager: DataStoreManager
) {
suspend fun changeCurrency(currentBalance: Balance, newCurrencyEnum: CurrencyEnum) = flow {
val uid = auth.currentUser?.uid
if (uid != null) {
emit(Result.InProgress)
println("here")
val convertedMainBalance = frankfurterApi.convert(
amount = currentBalance.balance,
from = CurrencyEnum.entries[currentBalance.currency].name,
to = newCurrencyEnum.name)
println("here2")
firestore.document(uid).collection("balance").document("balance")
.set(Balance(newCurrencyEnum.ordinal,
convertedMainBalance.rates[newCurrencyEnum.name]!!)).await()
changeLastTimeUpdated(Instant.now().toEpochMilli())
emit(Result.Success(""))
}
}
api:
interface FrankfurterApi {
@GET("latest")
suspend fun convert(
@Query("amount") amount: Float,
@Query("from") from: String,
@Query("to") to: String
): ExchangeCurrency